diff --git a/libropkg/CMakeLists.txt b/libropkg/CMakeLists.txt new file mode 100644 index 0000000..8ccc136 --- /dev/null +++ b/libropkg/CMakeLists.txt @@ -0,0 +1,14 @@ +file(GLOB sources *.c include/ropkg/*.h) + +add_library(libropkg SHARED ${sources}) + +set_target_properties(libropkg PROPERTIES + OUTPUT_NAME ropkg) +target_link_libraries(libropkg + ${ZSTD_LIBRARY}) +target_include_directories(libropkg PUBLIC + include/ + ${ZSTD_INCLUDE_DIR}) +target_compile_definitions(libropkg PRIVATE + LIBROPKG_EXPORT=1 + LIBROPKG_STATIC=0) diff --git a/libropkg/compress.c b/libropkg/compress.c new file mode 100644 index 0000000..f35764e --- /dev/null +++ b/libropkg/compress.c @@ -0,0 +1,86 @@ +#include "compress.h" + +#include +#include + +extern const struct ropkg_compression_function ropkg_zstd; + +static const struct ropkg_compression_function *functions[] = { + [ROPKG_COMPRESSION_ZSTD] = &ropkg_zstd, +}; +static const size_t nr_functions = sizeof functions / sizeof functions[0]; + +const struct ropkg_compression_function *ropkg_compression_function_for_type( + enum ropkg_compression_type type) +{ + if (type >= nr_functions) { + return NULL; + } + + return functions[type]; +} + +enum ropkg_status ropkg_compression_function_get_buffer_size( + const struct ropkg_compression_function *func, + enum ropkg_compression_stream_mode mode, + size_t *in_buffer_size, + size_t *out_buffer_size) +{ + if (!func->f_buffer_size) { + return ROPKG_ERR_NOT_SUPPORTED; + } + + return func->f_buffer_size(mode, in_buffer_size, out_buffer_size); +} + +const char *ropkg_compression_function_get_file_extension( + const struct ropkg_compression_function *func) +{ + return func->f_extension; +} + +enum ropkg_status ropkg_compression_stream_open( + const struct ropkg_compression_function *func, + void *in_buffer, + size_t in_max, + void *out_buffer, + size_t out_max, + enum ropkg_compression_stream_mode mode, + struct ropkg_compression_stream **out) +{ + if (!func->f_open) { + return ROPKG_ERR_NOT_SUPPORTED; + } + + return func->f_open( + func, + mode, + in_buffer, + in_max, + out_buffer, + out_max, + out); +} + +enum ropkg_status ropkg_compression_stream_process( + struct ropkg_compression_stream *stream, + size_t in_size, + enum ropkg_compression_stream_op op, + size_t *out_size) +{ + if (!stream->s_func->f_process) { + return ROPKG_ERR_NOT_SUPPORTED; + } + + return stream->s_func->f_process(stream, in_size, op, out_size); +} + +enum ropkg_status ropkg_compression_stream_close( + struct ropkg_compression_stream *stream) +{ + if (!stream->s_func->f_close) { + return ROPKG_ERR_NOT_SUPPORTED; + } + + return stream->s_func->f_close(stream); +} diff --git a/libropkg/compress.h b/libropkg/compress.h new file mode 100644 index 0000000..9c62d37 --- /dev/null +++ b/libropkg/compress.h @@ -0,0 +1,36 @@ +#ifndef COMPRESS_H_ +#define COMPRESS_H_ + +#include + +struct ropkg_compression_function { + const char *f_name; + const char *f_extension; + enum ropkg_status (*f_buffer_size)( + enum ropkg_compression_stream_mode, + size_t *, + size_t *); + enum ropkg_status (*f_open)( + const struct ropkg_compression_function *, + enum ropkg_compression_stream_mode, + void *, + size_t, + void *, + size_t, + struct ropkg_compression_stream **); + enum ropkg_status (*f_close)(struct ropkg_compression_stream *); + enum ropkg_status (*f_process)( + struct ropkg_compression_stream *, + size_t, + enum ropkg_compression_stream_op, + size_t *); +}; + +struct ropkg_compression_stream { + const struct ropkg_compression_function *s_func; + enum ropkg_compression_stream_mode s_mode; + void *s_in, *s_out; + size_t s_in_max, s_out_max; +}; + +#endif diff --git a/libropkg/include/ropkg/compress.h b/libropkg/include/ropkg/compress.h new file mode 100644 index 0000000..ae8e00e --- /dev/null +++ b/libropkg/include/ropkg/compress.h @@ -0,0 +1,53 @@ +#ifndef ROPKG_COMPRESS_H_ +#define ROPKG_COMPRESS_H_ + +#include +#include +#include + +struct ropkg_compression_function; +struct ropkg_compression_stream; + +enum ropkg_compression_stream_mode { + ROPKG_COMPRESSION_MODE_COMPRESS = 0, + ROPKG_COMPRESSION_MODE_DECOMPRESS, +}; + +enum ropkg_compression_stream_op { + ROPKG_COMPRESSION_OP_CONTINUE = 0, + ROPKG_COMPRESSION_OP_END, +}; + +enum ropkg_compression_type { + ROPKG_COMPRESSION_NONE = 0, + ROPKG_COMPRESSION_ZSTD, +}; + +ROPKG_API const struct ropkg_compression_function * +ropkg_compression_function_for_type(enum ropkg_compression_type type); + +ROPKG_API enum ropkg_status ropkg_compression_function_get_buffer_size( + const struct ropkg_compression_function *func, + enum ropkg_compression_stream_mode mode, + size_t *in_buffer_size, + size_t *out_buffer_size); +ROPKG_API const char *ropkg_compression_function_get_file_extension( + const struct ropkg_compression_function *func); + +ROPKG_API enum ropkg_status ropkg_compression_stream_open( + const struct ropkg_compression_function *func, + void *in_buffer, + size_t in_max, + void *out_buffer, + size_t out_max, + enum ropkg_compression_stream_mode mode, + struct ropkg_compression_stream **out); +ROPKG_API enum ropkg_status ropkg_compression_stream_process( + struct ropkg_compression_stream *stream, + size_t in_size, + enum ropkg_compression_stream_op op, + size_t *out_size); +ROPKG_API enum ropkg_status ropkg_compression_stream_close( + struct ropkg_compression_stream *stream); + +#endif diff --git a/libropkg/include/ropkg/misc.h b/libropkg/include/ropkg/misc.h new file mode 100644 index 0000000..3476f27 --- /dev/null +++ b/libropkg/include/ropkg/misc.h @@ -0,0 +1,18 @@ +#ifndef ROPKG_MISC_H_ +#define ROPKG_MISC_H_ + +#ifdef _MSC_VER +#ifdef LIBROPKG_STATIC +#define ROPKG_API extern +#else +#ifdef LIBROPKG_EXPORT +#define ROPKG_API extern __declspec(dllexport) +#else +#define ROPKG_API extern __declspec(dllimport) +#endif +#endif +#else +#define ROPKG_API extern +#endif + +#endif diff --git a/libropkg/include/ropkg/status.h b/libropkg/include/ropkg/status.h new file mode 100644 index 0000000..770b069 --- /dev/null +++ b/libropkg/include/ropkg/status.h @@ -0,0 +1,14 @@ +#ifndef ROPKG_STATUS_H_ +#define ROPKG_STATUS_H_ + +enum ropkg_status { + ROPKG_SUCCESS = 0, + ROPKG_ERR_NOT_SUPPORTED, + ROPKG_ERR_INVALID_ARGUMENT, + ROPKG_ERR_NO_MEMORY, + ROPKG_ERR_BAD_STATE, + ROPKG_ERR_INTERNAL_FAILURE, + ROPKG_ERR_IO_FAILURE, +}; + +#endif diff --git a/libropkg/include/ropkg/stream.h b/libropkg/include/ropkg/stream.h new file mode 100644 index 0000000..b2fdbc8 --- /dev/null +++ b/libropkg/include/ropkg/stream.h @@ -0,0 +1,58 @@ +#ifndef ROPKG_STREAM_H_ +#define ROPKG_STREAM_H_ + +#include +#include +#include +#include + +struct ropkg_stream; +struct ropkg_compression_function; + +enum ropkg_stream_mode { + ROPKG_STREAM_READ, + ROPKG_STREAM_WRITE, +}; + +ROPKG_API enum ropkg_status ropkg_stream_open( + FILE *fp, + const struct ropkg_compression_function *func, + enum ropkg_stream_mode mode, + struct ropkg_stream **out); +ROPKG_API enum ropkg_status ropkg_stream_close(struct ropkg_stream *stream); + +ROPKG_API enum ropkg_status ropkg_stream_begin_compressed_section( + struct ropkg_stream *stream); +ROPKG_API enum ropkg_status ropkg_stream_end_compressed_section( + struct ropkg_stream *stream, + size_t *out_nr_written_compressed, + size_t *out_nr_written_uncompressed); + +ROPKG_API enum ropkg_status ropkg_stream_read( + struct ropkg_stream *stream, + void *p, + size_t len, + size_t *nr_read); +ROPKG_API enum ropkg_status ropkg_stream_write( + struct ropkg_stream *stream, + const void *p, + size_t len, + size_t *nr_written); + +ROPKG_API size_t ropkg_stream_get_cursor_position(struct ropkg_stream *stream); +ROPKG_API enum ropkg_status ropkg_stream_set_cursor_position( + struct ropkg_stream *stream, + size_t pos); +ROPKG_API enum ropkg_status ropkg_stream_restore_cursor_position( + struct ropkg_stream *stream); + +ROPKG_API bool ropkg_stream_is_in_compressed_section( + struct ropkg_stream *stream); + +ROPKG_API size_t ropkg_stream_get_nr_written(struct ropkg_stream *stream); +ROPKG_API size_t +ropkg_stream_get_nr_written_compressed(struct ropkg_stream *stream); +ROPKG_API size_t +ropkg_stream_get_nr_written_uncompressed(struct ropkg_stream *stream); + +#endif diff --git a/libropkg/include/ropkg/writer.h b/libropkg/include/ropkg/writer.h new file mode 100644 index 0000000..1254ce7 --- /dev/null +++ b/libropkg/include/ropkg/writer.h @@ -0,0 +1,36 @@ +#ifndef ROPKG_WRITER_H_ +#define ROPKG_WRITER_H_ + +#include +#include +#include + +#define ROPKG_PATH_DATA "data.tar" +#define ROPKG_PATH_CONTROL "control.tar" +#define ROPKG_PATH_META "meta.tar" + +struct ropkg_writer; +struct ropkg_stream; + +struct ropkg_writer_file_info { + size_t f_length; +}; + +ROPKG_API enum ropkg_status ropkg_writer_open( + struct ropkg_stream *fp, + struct ropkg_writer **out); +ROPKG_API enum ropkg_status ropkg_writer_close(struct ropkg_writer *pkg); + +ROPKG_API enum ropkg_status ropkg_writer_begin_file( + struct ropkg_writer *pkg, + const char *path, + const struct ropkg_writer_file_info *info); +ROPKG_API enum ropkg_status ropkg_writer_end_file(struct ropkg_writer *pkg); + +ROPKG_API enum ropkg_status ropkg_writer_write( + struct ropkg_writer *pkg, + const void *p, + size_t len, + size_t *nr_written); + +#endif diff --git a/libropkg/status.c b/libropkg/status.c new file mode 100644 index 0000000..e69de29 diff --git a/libropkg/stream.c b/libropkg/stream.c new file mode 100644 index 0000000..6dd40b2 --- /dev/null +++ b/libropkg/stream.c @@ -0,0 +1,355 @@ +#include "stream.h" + +#include +#include +#include +#include + +enum ropkg_status ropkg_stream_open( + FILE *fp, + const struct ropkg_compression_function *func, + enum ropkg_stream_mode mode, + struct ropkg_stream **out) +{ + switch (mode) { + case ROPKG_STREAM_READ: + case ROPKG_STREAM_WRITE: + break; + default: + return ROPKG_ERR_INVALID_ARGUMENT; + } + + struct ropkg_stream *stream = malloc(sizeof *stream); + if (!stream) { + return ROPKG_ERR_NO_MEMORY; + } + + memset(stream, 0x0, sizeof *stream); + + stream->s_fp = fp; + stream->s_mode = mode; + + if (!func) { + *out = stream; + return ROPKG_SUCCESS; + } + + struct ropkg_stream_compressor *c = &stream->s_compressor; + enum ropkg_compression_stream_mode c_mode; + switch (mode) { + case ROPKG_STREAM_READ: + c_mode = ROPKG_COMPRESSION_MODE_DECOMPRESS; + break; + case ROPKG_STREAM_WRITE: + c_mode = ROPKG_COMPRESSION_MODE_COMPRESS; + break; + default: + /* this won't happen */ + return ROPKG_ERR_INVALID_ARGUMENT; + } + + enum ropkg_status status = ropkg_compression_function_get_buffer_size( + func, + c_mode, + &c->c_in_max, + &c->c_out_max); + if (status != ROPKG_SUCCESS) { + free(stream); + return status; + } + + c->c_in_buf = malloc(c->c_in_max); + if (!c->c_in_buf) { + free(stream); + return ROPKG_ERR_NO_MEMORY; + } + + c->c_out_buf = malloc(c->c_out_max); + if (!c->c_out_buf) { + free(c->c_in_buf); + free(stream); + return ROPKG_ERR_NO_MEMORY; + } + + status = ropkg_compression_stream_open( + func, + c->c_in_buf, + c->c_in_max, + c->c_out_buf, + c->c_out_max, + c_mode, + &c->c_stream); + + if (status != ROPKG_SUCCESS) { + free(c->c_in_buf); + free(c->c_out_buf); + free(stream); + return status; + } + + c->c_func = func; + + *out = stream; + return ROPKG_SUCCESS; +} + +enum ropkg_status ropkg_stream_close(struct ropkg_stream *stream) +{ + while (stream->s_compression_depth > 0) { + ropkg_stream_end_compressed_section(stream, NULL, NULL); + } + + if (stream->s_compressor.c_in_buf) { + free(stream->s_compressor.c_in_buf); + } + + if (stream->s_compressor.c_out_buf) { + free(stream->s_compressor.c_out_buf); + } + + if (stream->s_compressor.c_stream) { + ropkg_compression_stream_close(stream->s_compressor.c_stream); + } + + free(stream); + + return ROPKG_SUCCESS; +} + +enum ropkg_status ropkg_stream_begin_compressed_section( + struct ropkg_stream *stream) +{ + if (stream->s_flags & ROPKG_STREAM_F_CURSOR_MOVED) { + return ROPKG_ERR_BAD_STATE; + } + + if (stream->s_compression_depth > 0) { + stream->s_compression_depth++; + return ROPKG_SUCCESS; + } + + stream->s_compression_depth = 1; + stream->s_compressor.c_in_count = 0; + stream->s_compressor.c_nr_written_compressed = 0; + stream->s_compressor.c_nr_written_uncompressed = 0; + + return ROPKG_SUCCESS; +} + +enum ropkg_status ropkg_stream_end_compressed_section( + struct ropkg_stream *stream, + size_t *out_nr_written_compressed, + size_t *out_nr_written_uncompressed) +{ + if (stream->s_flags & ROPKG_STREAM_F_CURSOR_MOVED) { + return ROPKG_ERR_BAD_STATE; + } + + if (stream->s_compression_depth == 0) { + return ROPKG_ERR_BAD_STATE; + } + + if (stream->s_compression_depth > 1) { + stream->s_compression_depth--; + return ROPKG_SUCCESS; + } + + stream->s_compression_depth = 0; + struct ropkg_stream_compressor *c = &stream->s_compressor; + if (out_nr_written_compressed) { + *out_nr_written_compressed = c->c_nr_written_compressed; + } + + if (out_nr_written_uncompressed) { + *out_nr_written_uncompressed = c->c_nr_written_uncompressed; + } + + if (c->c_nr_written_uncompressed == 0) { + return ROPKG_SUCCESS; + } + + size_t out_size = 0; + + ropkg_compression_stream_process( + c->c_stream, + c->c_in_count, + ROPKG_COMPRESSION_OP_END, + &out_size); + + size_t nr_written = fwrite(c->c_out_buf, 1, out_size, stream->s_fp); + if (nr_written != out_size) { + return ROPKG_ERR_IO_FAILURE; + } + + c->c_nr_written_compressed += nr_written; + stream->s_nr_written += out_size; + if (out_nr_written_compressed) { + *out_nr_written_compressed = c->c_nr_written_compressed; + } + + stream->s_compressor.c_in_count = 0; + stream->s_compressor.c_nr_written_compressed = 0; + stream->s_compressor.c_nr_written_uncompressed = 0; + + return ROPKG_SUCCESS; +} + +enum ropkg_status ropkg_stream_read( + struct ropkg_stream *stream, + void *p, + size_t len, + size_t *nr_read) +{ + if (stream->s_mode != ROPKG_STREAM_READ) { + return ROPKG_ERR_BAD_STATE; + } + + return ROPKG_SUCCESS; +} + +static enum ropkg_status flush_compressed_buffer(struct ropkg_stream *stream) +{ + struct ropkg_stream_compressor *c = &stream->s_compressor; + + size_t out_size; + enum ropkg_status status = ropkg_compression_stream_process( + c->c_stream, + c->c_in_count, + ROPKG_COMPRESSION_OP_CONTINUE, + &out_size); + + if (status != ROPKG_SUCCESS) { + return status; + } + + size_t written = fwrite(c->c_out_buf, 1, out_size, stream->s_fp); + + c->c_nr_written_compressed += c->c_in_count; + stream->s_nr_written_compressed = c->c_in_count; + stream->s_nr_written += out_size; + + return ROPKG_SUCCESS; +} + +enum ropkg_status ropkg_stream_write( + struct ropkg_stream *stream, + const void *p, + size_t len, + size_t *nr_written) +{ + if (stream->s_mode != ROPKG_STREAM_WRITE) { + return ROPKG_ERR_BAD_STATE; + } + + size_t w = 0; + if (stream->s_compression_depth == 0) { + w = fwrite(p, 1, len, stream->s_fp); + + *nr_written = w; + if (w != len) { + return ROPKG_ERR_IO_FAILURE; + } + + if (!(stream->s_flags & ROPKG_STREAM_F_CURSOR_MOVED)) { + stream->s_nr_written_uncompressed += w; + stream->s_nr_written += w; + } + + return ROPKG_SUCCESS; + } + + if (stream->s_flags & ROPKG_STREAM_F_CURSOR_MOVED) { + return ROPKG_ERR_BAD_STATE; + } + + struct ropkg_stream_compressor *c = &stream->s_compressor; + const unsigned char *src = p; + enum ropkg_status status = ROPKG_SUCCESS; + + size_t remaining = len; + while (remaining > 0 && status == ROPKG_SUCCESS) { + unsigned char *dest = c->c_in_buf + c->c_in_count; + size_t space_remaining = c->c_in_max - c->c_in_count; + + size_t to_copy = remaining; + if (to_copy > space_remaining) { + to_copy = space_remaining; + } + + memcpy(dest, src, to_copy); + + c->c_in_count += to_copy; + c->c_nr_written_uncompressed += to_copy; + stream->s_nr_written_uncompressed += to_copy; + space_remaining -= to_copy; + remaining -= to_copy; + w += to_copy; + + if (space_remaining == 0) { + status = flush_compressed_buffer(stream); + } + } + + *nr_written = w; + return status; +} + +size_t ropkg_stream_get_cursor_position(struct ropkg_stream *stream) +{ + return ftell(stream->s_fp); +} + +enum ropkg_status ropkg_stream_set_cursor_position( + struct ropkg_stream *stream, + size_t pos) +{ + if (stream->s_flags & ROPKG_STREAM_F_CURSOR_MOVED) { + return ROPKG_ERR_BAD_STATE; + } + + if (stream->s_compression_depth > 0) { + return ROPKG_ERR_BAD_STATE; + } + + stream->s_saved_cursor = ftell(stream->s_fp); + fseek(stream->s_fp, pos, SEEK_SET); + + stream->s_flags |= ROPKG_STREAM_F_CURSOR_MOVED; + + return ROPKG_SUCCESS; +} + +enum ropkg_status ropkg_stream_restore_cursor_position( + struct ropkg_stream *stream) +{ + if (!(stream->s_flags & ROPKG_STREAM_F_CURSOR_MOVED)) { + return ROPKG_ERR_BAD_STATE; + } + + fseek(stream->s_fp, stream->s_saved_cursor, SEEK_SET); + stream->s_saved_cursor = 0; + + stream->s_flags &= ~ROPKG_STREAM_F_CURSOR_MOVED; + + return ROPKG_SUCCESS; +} + +bool ropkg_stream_is_in_compressed_section(struct ropkg_stream *stream) +{ + return stream->s_compression_depth > 0; +} + +size_t ropkg_stream_get_nr_written(struct ropkg_stream *stream) +{ + return stream->s_nr_written; +} + +size_t ropkg_stream_get_nr_written_compressed(struct ropkg_stream *stream) +{ + return stream->s_nr_written_compressed; +} + +size_t ropkg_stream_get_nr_written_uncompressed(struct ropkg_stream *stream) +{ + return stream->s_nr_written_uncompressed; +} diff --git a/libropkg/stream.h b/libropkg/stream.h new file mode 100644 index 0000000..322cdd4 --- /dev/null +++ b/libropkg/stream.h @@ -0,0 +1,39 @@ +#ifndef _ROPKG_STREAM_H_ +#define _ROPKG_STREAM_H_ + +#include +#include + +struct ropkg_compression_function; +struct ropkg_compression_stream; + +enum ropkg_stream_flags { + ROPKG_STREAM_F_CURSOR_MOVED = 0x01u, +}; + +struct ropkg_stream_compressor { + const struct ropkg_compression_function *c_func; + struct ropkg_compression_stream *c_stream; + + size_t c_in_max, c_out_max; + void *c_in_buf, *c_out_buf; + size_t c_in_count; + + size_t c_nr_written_compressed, c_nr_written_uncompressed; +}; + +struct ropkg_stream { + FILE *s_fp; + enum ropkg_stream_flags s_flags; + enum ropkg_stream_mode s_mode; + + unsigned int s_compression_depth; + struct ropkg_stream_compressor s_compressor; + + size_t s_saved_cursor; + size_t s_nr_written; + size_t s_nr_written_compressed; + size_t s_nr_written_uncompressed; +}; + +#endif diff --git a/libropkg/writer.c b/libropkg/writer.c new file mode 100644 index 0000000..6eafeec --- /dev/null +++ b/libropkg/writer.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include + +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; +} diff --git a/libropkg/zstd.c b/libropkg/zstd.c new file mode 100644 index 0000000..167cde8 --- /dev/null +++ b/libropkg/zstd.c @@ -0,0 +1,203 @@ +#include "compress.h" + +#include +#include +#include + +struct zstd_stream { + struct ropkg_compression_stream s_base; + union { + ZSTD_CCtx *c; + ZSTD_DCtx *d; + } s_ctx; +}; + +static enum ropkg_status zstd_buffer_size( + enum ropkg_compression_stream_mode mode, + size_t *in_buffer_size, + size_t *out_buffer_size) +{ + switch (mode) { + case ROPKG_COMPRESSION_MODE_COMPRESS: + if (in_buffer_size) { + *in_buffer_size = ZSTD_CStreamInSize(); + } + + if (out_buffer_size) { + *out_buffer_size = ZSTD_CStreamOutSize(); + } + return ROPKG_SUCCESS; + case ROPKG_COMPRESSION_MODE_DECOMPRESS: + if (in_buffer_size) { + *in_buffer_size = ZSTD_DStreamInSize(); + } + + if (out_buffer_size) { + *out_buffer_size = ZSTD_DStreamOutSize(); + } + return ROPKG_SUCCESS; + default: + return ROPKG_ERR_INVALID_ARGUMENT; + } +} + +static enum ropkg_status zstd_open( + const struct ropkg_compression_function *func, + enum ropkg_compression_stream_mode mode, + void *in_buffer, + size_t in_max, + void *out_buffer, + size_t out_max, + struct ropkg_compression_stream **out) +{ + struct zstd_stream *stream = malloc(sizeof *stream); + if (!stream) { + return ROPKG_ERR_NO_MEMORY; + } + + memset(stream, 0x0, sizeof *stream); + + stream->s_base.s_in = in_buffer; + stream->s_base.s_in_max = in_max; + stream->s_base.s_out = out_buffer; + stream->s_base.s_out_max = out_max; + stream->s_base.s_func = func; + stream->s_base.s_mode = mode; + + switch (mode) { + case ROPKG_COMPRESSION_MODE_COMPRESS: + stream->s_ctx.c = ZSTD_createCCtx(); + + if (!stream->s_ctx.c) { + free(stream); + return ROPKG_ERR_NO_MEMORY; + } + + ZSTD_CCtx_setParameter( + stream->s_ctx.c, + ZSTD_c_compressionLevel, + 10); + ZSTD_CCtx_setParameter(stream->s_ctx.c, ZSTD_c_checksumFlag, 1); + break; + case ROPKG_COMPRESSION_MODE_DECOMPRESS: + stream->s_ctx.d = ZSTD_createDCtx(); + + if (!stream->s_ctx.d) { + free(stream); + return ROPKG_ERR_NO_MEMORY; + } + + break; + default: + free(stream); + return ROPKG_ERR_INVALID_ARGUMENT; + } + + *out = &stream->s_base; + return ROPKG_SUCCESS; +} + +static enum ropkg_status zstd_close(struct ropkg_compression_stream *stream) +{ + struct zstd_stream *zstream = (struct zstd_stream *)stream; + switch (stream->s_mode) { + case ROPKG_COMPRESSION_MODE_COMPRESS: + ZSTD_freeCCtx(zstream->s_ctx.c); + break; + case ROPKG_COMPRESSION_MODE_DECOMPRESS: + ZSTD_freeDCtx(zstream->s_ctx.d); + break; + default: + return ROPKG_ERR_BAD_STATE; + } + + free(stream); + return ROPKG_SUCCESS; +} + +static enum ropkg_status zstd_compress( + struct zstd_stream *zstream, + enum ropkg_compression_stream_op op, + ZSTD_inBuffer *in, + ZSTD_outBuffer *out) +{ + ZSTD_EndDirective zop; + + switch (op) { + case ROPKG_COMPRESSION_OP_CONTINUE: + zop = ZSTD_e_continue; + break; + case ROPKG_COMPRESSION_OP_END: + zop = ZSTD_e_end; + break; + default: + return ROPKG_ERR_INVALID_ARGUMENT; + } + + do { + size_t ret + = ZSTD_compressStream2(zstream->s_ctx.c, out, in, zop); + + if (ret == 0) { + break; + } + } while (in->pos < in->size); + + return ROPKG_SUCCESS; +} + +static enum ropkg_status zstd_decompress( + struct zstd_stream *zstream, + ZSTD_inBuffer *in, + ZSTD_outBuffer *out) +{ + size_t ret = 0; + + while (in->pos < in->size) { + ret = ZSTD_decompressStream(zstream->s_ctx.d, out, in); + } + + if (ret != 0) { + return ROPKG_ERR_INTERNAL_FAILURE; + } + + return ROPKG_SUCCESS; +} + +static enum ropkg_status zstd_process( + struct ropkg_compression_stream *stream, + size_t in_size, + enum ropkg_compression_stream_op op, + size_t *out_size) +{ + struct zstd_stream *zstream = (struct zstd_stream *)stream; + ZSTD_inBuffer in = {.src = stream->s_in, .size = in_size}; + ZSTD_outBuffer out = {.dst = stream->s_out, .size = stream->s_out_max}; + + enum ropkg_status status; + switch (stream->s_mode) { + case ROPKG_COMPRESSION_MODE_COMPRESS: + status = zstd_compress(zstream, op, &in, &out); + break; + case ROPKG_COMPRESSION_MODE_DECOMPRESS: + status = zstd_decompress(zstream, &in, &out); + break; + default: + return ROPKG_ERR_BAD_STATE; + } + + if (out_size) { + *out_size = out.pos; + } + + return ROPKG_SUCCESS; +} + +const struct ropkg_compression_function ropkg_zstd = { + .f_name = "zstd", + .f_extension = ".zst", + .f_buffer_size = zstd_buffer_size, + .f_open = zstd_open, + .f_close = zstd_close, + .f_process = zstd_process, +};