#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; }