/* createrepo_c - Library of routines for manipulation with repodata * Copyright (C) 2013 Tomas Mlcoch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include #include #include #include #include "xml_file.h" #include #include "error.h" #include "xml_dump.h" #include "compression_wrapper.h" #include "xml_dump_internal.h" #include "misc.h" #define ERR_DOMAIN CREATEREPO_C_ERROR #define XML_HEADER "\n" #define XML_PRIMARY_HEADER XML_HEADER"\n" #define XML_FILELISTS_HEADER XML_HEADER"\n" #define XML_OTHER_HEADER XML_HEADER"\n" #define XML_PRESTODELTA_HEADER XML_HEADER"\n" #define XML_UPDATEINFO_HEADER XML_HEADER"\n" #define XML_MAX_HEADER_SIZE 300 #define XML_RECOMPRESS_BUFFER_SIZE 8192 #define XML_PRIMARY_FOOTER "" #define XML_FILELISTS_FOOTER "" #define XML_OTHER_FOOTER "" #define XML_PRESTODELTA_FOOTER "" #define XML_UPDATEINFO_FOOTER "" cr_XmlFile * cr_xmlfile_sopen(const char *filename, cr_XmlFileType type, cr_CompressionType comtype, cr_ContentStat *stat, GError **err) { cr_XmlFile *f; GError *tmp_err = NULL; assert(filename); assert(type < CR_XMLFILE_SENTINEL); assert(comtype < CR_CW_COMPRESSION_SENTINEL); assert(!err || *err == NULL); if (g_file_test(filename, G_FILE_TEST_EXISTS)) { g_set_error(err, ERR_DOMAIN, CRE_EXISTS, "File already exists"); return NULL; } CR_FILE *cr_f = cr_sopen(filename, CR_CW_MODE_WRITE, comtype, stat, &tmp_err); if (!cr_f) { g_propagate_prefixed_error(err, tmp_err, "Cannot open %s: ", filename); return NULL; } f = g_new0(cr_XmlFile, 1); f->f = cr_f; f->type = type; f->header = 0; f->footer = 0; f->pkgs = 0; return f; } int cr_xmlfile_set_num_of_pkgs(cr_XmlFile *f, long num, GError **err) { assert(f); assert(!err || *err == NULL); if (f->header != 0) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Header was already written"); return CRE_BADARG; } if (num < 0) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "The number must be a positive integer number"); return CRE_BADARG; } f->pkgs = num; return CRE_OK; } int cr_xmlfile_write_xml_header(cr_XmlFile *f, GError **err) { const char *xml_header; GError *tmp_err = NULL; assert(f); assert(!err || *err == NULL); assert(f->header == 0); switch (f->type) { case CR_XMLFILE_PRIMARY: xml_header = XML_PRIMARY_HEADER; break; case CR_XMLFILE_FILELISTS: xml_header = XML_FILELISTS_HEADER; break; case CR_XMLFILE_OTHER: xml_header = XML_OTHER_HEADER; break; case CR_XMLFILE_PRESTODELTA: xml_header = XML_PRESTODELTA_HEADER; break; case CR_XMLFILE_UPDATEINFO: xml_header = XML_UPDATEINFO_HEADER; break; default: g_critical("%s: Bad file type", __func__); assert(0); g_set_error(err, ERR_DOMAIN, CRE_ASSERT, "Bad file type"); return CRE_ASSERT; } if (cr_printf(&tmp_err, f->f, xml_header, f->pkgs) == CR_CW_ERR) { int code = tmp_err->code; g_propagate_prefixed_error(err, tmp_err, "Cannot write XML header: "); return code; } f->header = 1; return cr_end_chunk(f->f, err); } int cr_xmlfile_write_xml_footer(cr_XmlFile *f, GError **err) { const char *xml_footer; GError *tmp_err = NULL; assert(f); assert(!err || *err == NULL); assert(f->footer == 0); switch (f->type) { case CR_XMLFILE_PRIMARY: xml_footer = XML_PRIMARY_FOOTER; break; case CR_XMLFILE_FILELISTS: xml_footer = XML_FILELISTS_FOOTER; break; case CR_XMLFILE_OTHER: xml_footer = XML_OTHER_FOOTER; break; case CR_XMLFILE_PRESTODELTA: xml_footer = XML_PRESTODELTA_FOOTER; break; case CR_XMLFILE_UPDATEINFO: xml_footer = XML_UPDATEINFO_FOOTER; break; default: g_critical("%s: Bad file type", __func__); assert(0); g_set_error(err, ERR_DOMAIN, CRE_ASSERT, "Bad file type"); return CRE_ASSERT; } cr_puts(f->f, xml_footer, &tmp_err); if (tmp_err) { int code = tmp_err->code; g_propagate_prefixed_error(err, tmp_err, "Cannot write XML footer: "); return code; } f->footer = 1; return CRE_OK; } int cr_xmlfile_add_pkg(cr_XmlFile *f, cr_Package *pkg, GError **err) { char *xml; GError *tmp_err = NULL; assert(f); assert(pkg); assert(!err || *err == NULL); assert(f->footer == 0); switch (f->type) { case CR_XMLFILE_PRIMARY: xml = cr_xml_dump_primary(pkg, &tmp_err); break; case CR_XMLFILE_FILELISTS: xml = cr_xml_dump_filelists(pkg, &tmp_err); break; case CR_XMLFILE_OTHER: xml = cr_xml_dump_other(pkg, &tmp_err); break; default: g_critical("%s: Bad file type", __func__); assert(0); g_set_error(err, ERR_DOMAIN, CRE_ASSERT, "Bad file type"); return CRE_ASSERT; } if (tmp_err) { int code = tmp_err->code; g_propagate_error(err, tmp_err); if (xml) g_free(xml); return code; } if (xml) { cr_xmlfile_add_chunk(f, xml, &tmp_err); g_free(xml); if (tmp_err) { int code = tmp_err->code; g_propagate_error(err, tmp_err); return code; } } return CRE_OK; } int cr_xmlfile_add_chunk(cr_XmlFile *f, const char* chunk, GError **err) { GError *tmp_err = NULL; assert(f); assert(!err || *err == NULL); assert(f->footer == 0); if (!chunk) return CRE_OK; if (f->header == 0) { cr_xmlfile_write_xml_header(f, &tmp_err); if (tmp_err) { int code = tmp_err->code; g_propagate_error(err, tmp_err); return code; } } cr_puts(f->f, chunk, &tmp_err); if (tmp_err) { int code = tmp_err->code; g_propagate_prefixed_error(err, tmp_err, "Error while write: "); return code; } return CRE_OK; } int cr_xmlfile_close(cr_XmlFile *f, GError **err) { GError *tmp_err = NULL; assert(!err || *err == NULL); if (!f) return CRE_OK; if (f->header == 0) { cr_xmlfile_write_xml_header(f, &tmp_err); if (tmp_err) { int code = tmp_err->code; g_propagate_error(err, tmp_err); return code; } } if (f->footer == 0) { cr_xmlfile_write_xml_footer(f, &tmp_err); if (tmp_err) { int code = tmp_err->code; g_propagate_error(err, tmp_err); return code; } } cr_close(f->f, &tmp_err); if (tmp_err) { int code = tmp_err->code; g_propagate_prefixed_error(err, tmp_err, "Error while closing a file: "); return code; } g_free(f); return CRE_OK; } static int write_modified_header(int task_count, int package_count, cr_XmlFile *cr_file, gchar *header_buf, int header_len, GError **err) { GError *tmp_err = NULL; gchar *package_count_string; gchar *task_count_string; int bytes_written = 0; int package_count_string_len = rasprintf(&package_count_string, "packages=\"%i\"", package_count); int task_count_string_len = rasprintf(&task_count_string, "packages=\"%i\"", task_count); gchar *pointer_to_pkgs = g_strstr_len(header_buf, header_len, task_count_string); if (!pointer_to_pkgs){ g_free(package_count_string); g_free(task_count_string); return 0; } gchar *pointer_to_pkgs_end = pointer_to_pkgs + task_count_string_len; bytes_written += cr_write(cr_file->f, header_buf, pointer_to_pkgs - header_buf, &tmp_err); if (!tmp_err) bytes_written += cr_write(cr_file->f, package_count_string, package_count_string_len, &tmp_err); if (!tmp_err) bytes_written += cr_write(cr_file->f, pointer_to_pkgs_end, header_len - (pointer_to_pkgs_end - header_buf), &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while writing header part:"); g_free(package_count_string); g_free(task_count_string); return 0; } g_free(package_count_string); g_free(task_count_string); return bytes_written; } void cr_rewrite_header_package_count(gchar *original_filename, cr_CompressionType xml_compression, int package_count, int task_count, cr_ContentStat *file_stat, gchar *zck_dict_file, GError **err) { GError *tmp_err = NULL; CR_FILE *original_file = cr_open(original_filename, CR_CW_MODE_READ, CR_CW_AUTO_DETECT_COMPRESSION, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while reopening for reading:"); return; } gchar *tmp_xml_filename = g_strconcat(original_filename, ".tmp", NULL); cr_XmlFile *new_file = cr_xmlfile_sopen_primary(tmp_xml_filename, xml_compression, file_stat, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while opening for writing:"); cr_close(original_file, NULL); g_free(tmp_xml_filename); cr_xmlfile_close(new_file, NULL); return; } // We want to keep identical zchunk chunk sizes, therefore we copy by chunk if (xml_compression == CR_CW_ZCK_COMPRESSION) { if (zck_dict_file){ gchar *zck_dict = NULL; size_t zck_dict_size = 0; if (g_file_get_contents(zck_dict_file, &zck_dict, &zck_dict_size, &tmp_err)){ cr_set_dict(new_file->f, zck_dict, zck_dict_size, &tmp_err); } else { g_propagate_prefixed_error(err, tmp_err, "Error encountered setting zck dict:"); cr_xmlfile_close(new_file, NULL); cr_close(original_file, NULL); g_free(tmp_xml_filename); return; } } char *copy_buf = NULL; // Chunk with index 0 is dictionary, data (xml metadata and our header) starts at 1 ssize_t zchunk_index = 1; ssize_t len_read = cr_get_zchunk_with_index(original_file, zchunk_index, ©_buf, &tmp_err); if (!tmp_err) write_modified_header(task_count, package_count, new_file, copy_buf, len_read, &tmp_err); if (tmp_err){ g_propagate_prefixed_error(err, tmp_err, "Error encountered while recompressing:"); cr_xmlfile_close(new_file, NULL); cr_close(original_file, NULL); g_free(tmp_xml_filename); return; } zchunk_index++; while(len_read){ g_free(copy_buf); len_read = cr_get_zchunk_with_index(original_file, zchunk_index, ©_buf, &tmp_err); if (!tmp_err) cr_write(new_file->f, copy_buf, len_read, &tmp_err); if (!tmp_err) cr_end_chunk(new_file->f, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while recompressing:"); cr_xmlfile_close(new_file, NULL); cr_close(original_file, NULL); g_free(tmp_xml_filename); return; } zchunk_index++; } } else { gchar header_buf[XML_MAX_HEADER_SIZE]; int len_read = cr_read(original_file, header_buf, XML_MAX_HEADER_SIZE, &tmp_err); if (!tmp_err) write_modified_header(task_count, package_count, new_file, header_buf, len_read, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while recompressing:"); cr_xmlfile_close(new_file, NULL); cr_close(original_file, NULL); g_free(tmp_xml_filename); return; } //Copy the rest of the file gchar copy_buf[XML_RECOMPRESS_BUFFER_SIZE]; while(len_read) { len_read = cr_read(original_file, copy_buf, XML_RECOMPRESS_BUFFER_SIZE, &tmp_err); if (!tmp_err) cr_write(new_file->f, copy_buf, len_read, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while recompressing:"); cr_xmlfile_close(new_file, NULL); cr_close(original_file, NULL); g_free(tmp_xml_filename); return; } } } new_file->header = 1; new_file->footer = 1; cr_xmlfile_close(new_file, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while writing:"); cr_close(original_file, NULL); g_free(tmp_xml_filename); return; } cr_close(original_file, &tmp_err); if (tmp_err) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while writing:"); g_free(tmp_xml_filename); return; } if (!cr_move_recursive(tmp_xml_filename, original_filename, &tmp_err)) { g_propagate_prefixed_error(err, tmp_err, "Error encountered while renaming:"); g_free(tmp_xml_filename); return; } g_free(tmp_xml_filename); }