Files
ropkg/libropkg/writer.c

225 lines
4.8 KiB
C

#include <ropkg/compress.h>
#include <ropkg/stream.h>
#include <ropkg/writer.h>
#include <stdlib.h>
#include <string.h>
struct ustar_header {
char tar_filename[100];
char tar_mode[8];
char tar_uid[8];
char tar_gid[8];
char tar_filesize[12];
char tar_mtime[12];
char tar_checksum[8];
char tar_type[1];
char tar_linkname[100];
char tar_ustar_id[6];
char tar_ustar_version[2];
char tar_owner_name[32];
char tar_owner_group[32];
char tar_dev_major[8];
char tar_dev_minor[8];
char tar_filename_prefix[155];
char tar_padding[12];
};
struct ropkg_writer {
struct ropkg_stream *w_fp;
struct ustar_header w_file_header;
size_t w_file_header_offset;
size_t w_file_nr_written;
};
static const char zero_padding[512] = {0};
enum ropkg_status ropkg_writer_open(
struct ropkg_stream *fp,
struct ropkg_writer **out)
{
struct ropkg_writer *writer = malloc(sizeof *writer);
if (!writer) {
return ROPKG_ERR_NO_MEMORY;
}
memset(writer, 0x0, sizeof *writer);
writer->w_fp = fp;
*out = writer;
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_writer_close(struct ropkg_writer *pkg)
{
memset(&pkg->w_file_header, 0x0, sizeof pkg->w_file_header);
size_t nr_written;
enum ropkg_status status = ropkg_stream_write(
pkg->w_fp,
&pkg->w_file_header,
sizeof pkg->w_file_header,
&nr_written);
if (status != ROPKG_SUCCESS) {
return status;
}
status = ropkg_stream_write(
pkg->w_fp,
&pkg->w_file_header,
sizeof pkg->w_file_header,
&nr_written);
if (status != ROPKG_SUCCESS) {
return status;
}
if (nr_written != sizeof pkg->w_file_header) {
return ROPKG_ERR_IO_FAILURE;
}
free(pkg);
return ROPKG_SUCCESS;
}
static void to_octal(size_t in, char *out, size_t field_size)
{
// char buf[64];
snprintf(out, field_size, "%0*zo", (int)(field_size - 1), in);
// memcpy(out, buf, field_size);
}
static void refresh_header_checksum(struct ustar_header *header)
{
memset(header->tar_checksum, ' ', sizeof header->tar_checksum);
size_t checksum = 0;
unsigned char *v = (unsigned char *)header;
for (size_t i = 0; i < sizeof *header; i++) {
checksum += v[i];
}
to_octal(checksum, header->tar_checksum, sizeof header->tar_checksum);
}
static enum ropkg_status encode_header(
struct ropkg_writer *pkg,
const char *path,
const struct ropkg_writer_file_info *info,
struct ustar_header *out)
{
memset(out, 0x0, sizeof *out);
snprintf(out->tar_filename, sizeof out->tar_filename, "%s", path);
to_octal(info->f_length, out->tar_filesize, sizeof out->tar_filesize);
to_octal(0755, out->tar_mode, sizeof out->tar_mode);
out->tar_type[0] = '0';
snprintf(out->tar_ustar_id, sizeof out->tar_ustar_id, "ustar");
out->tar_ustar_version[0] = out->tar_ustar_version[1] = '0';
refresh_header_checksum(out);
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_writer_begin_file(
struct ropkg_writer *pkg,
const char *path,
const struct ropkg_writer_file_info *info)
{
if (ropkg_stream_is_in_compressed_section(pkg->w_fp)) {
pkg->w_file_header_offset
= ropkg_stream_get_nr_written_uncompressed(pkg->w_fp);
} else {
pkg->w_file_header_offset
= ropkg_stream_get_nr_written(pkg->w_fp);
}
enum ropkg_status status
= encode_header(pkg, path, info, &pkg->w_file_header);
if (status != ROPKG_SUCCESS) {
return status;
}
size_t nr_written;
status = ropkg_stream_write(
pkg->w_fp,
&pkg->w_file_header,
sizeof pkg->w_file_header,
&nr_written);
if (status != ROPKG_SUCCESS) {
return status;
}
if (nr_written != sizeof pkg->w_file_header) {
return ROPKG_ERR_IO_FAILURE;
}
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_writer_end_file(struct ropkg_writer *pkg)
{
enum ropkg_status status;
size_t written, file_length, pos;
if (ropkg_stream_is_in_compressed_section(pkg->w_fp)) {
pos = ropkg_stream_get_nr_written_uncompressed(pkg->w_fp);
} else {
pos = ropkg_stream_get_nr_written(pkg->w_fp);
}
file_length
= pos - pkg->w_file_header_offset - sizeof pkg->w_file_header;
size_t required_padding = 512 - (file_length % 512);
status = ropkg_stream_write(
pkg->w_fp,
zero_padding,
required_padding,
&written);
if (status != ROPKG_SUCCESS) {
return status;
}
to_octal(
file_length,
pkg->w_file_header.tar_filesize,
sizeof pkg->w_file_header.tar_filesize);
refresh_header_checksum(&pkg->w_file_header);
status = ropkg_stream_set_cursor_position(
pkg->w_fp,
pkg->w_file_header_offset);
if (status == ROPKG_SUCCESS) {
ropkg_stream_write(
pkg->w_fp,
&pkg->w_file_header,
sizeof pkg->w_file_header,
&written);
ropkg_stream_restore_cursor_position(pkg->w_fp);
}
return ROPKG_SUCCESS;
}
enum ropkg_status ropkg_writer_write(
struct ropkg_writer *pkg,
const void *p,
size_t len,
size_t *nr_written)
{
enum ropkg_status status
= ropkg_stream_write(pkg->w_fp, p, len, nr_written);
if (status != ROPKG_SUCCESS) {
return status;
}
pkg->w_file_nr_written += len;
return ROPKG_SUCCESS;
}