COMPREPLY=( $( compgen -W "bz2 gz xz" -- "$2" ) )
}
+_cr_checksum_type()
+{
+ COMPREPLY=( $( compgen -W "md5 sha sha1 sha224 sha256 sha384 sha512" -- "$2" ) )
+}
+
_cr_createrepo()
{
COMPREPLY=()
return 0
;;
-s|--checksum)
- COMPREPLY=( $( compgen -W 'md5 sha sha1 sha224 sha256 sha384 sha512' -- "$2" ) )
+ _cr_checksum_type "$1" "$2"
return 0
;;
-i|--pkglist|--read-pkgs-list)
} &&
complete -F _cr_mergerepo -o filenames mergerepo_c
+_cr_modifyrepo()
+{
+ COMPREPLY=()
+
+ case $3 in
+ --version|-h|--help|-a|--archlist|--unique-md-filenames|--simple-md-filenames|--verbose)
+ return 0
+ ;;
+ -f|--batchfile)
+ COMPREPLY=( $( compgen -f -o plusdirs -- "$2" ) )
+ return 0
+ ;;
+ --compress-type)
+ _cr_compress_type "" "$2"
+ return 0
+ ;;
+ -s|--checksum)
+ _cr_checksum_type "$1" "$2"
+ return 0
+ ;;
+ esac
+
+ COMPREPLY=( $( compgen -W '--version --help --mdtype --remove
+ --compress --no-compress --compress-type --checksum
+ --unique-md-filenames --simple-md-filenames
+ --verbose --batchfile --new-name' -- "$2" ) )
+} &&
+complete -F _cr_modifyrepo -o filenames modifyrepo_c
+
# Local variables:
# mode: shell-script
# sh-basic-offset: 4
load_metadata.c
locate_metadata.c
misc.c
+ modifyrepo_shared.c
package.c
parsehdr.c
parsepkg.c
load_metadata.h
locate_metadata.h
misc.h
+ modifyrepo_shared.h
package.h
parsehdr.h
parsepkg.h
${GLIB2_LIBRARIES}
${GTHREAD2_LIBRARIES})
+ADD_EXECUTABLE(modifyrepo_c modifyrepo_c.c)
+TARGET_LINK_LIBRARIES(modifyrepo_c
+ libcreaterepo_c
+ ${GLIB2_LIBRARIES}
+ ${GTHREAD2_LIBRARIES})
+
CONFIGURE_FILE("createrepo_c.pc.cmake" "${CMAKE_SOURCE_DIR}/src/createrepo_c.pc" @ONLY)
CONFIGURE_FILE("version.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/version.h" @ONLY)
INSTALL(TARGETS libcreaterepo_c LIBRARY DESTINATION ${LIB_INSTALL_DIR})
INSTALL(TARGETS createrepo_c DESTINATION bin/)
INSTALL(TARGETS mergerepo_c DESTINATION bin/)
+INSTALL(TARGETS modifyrepo_c DESTINATION bin/)
ADD_SUBDIRECTORY(python)
return g_quark_from_static_string("cr_misc_error");
}
+GQuark cr_modifyrepo_error_quark(void)
+{
+ return g_quark_from_static_string("cr_modifyrepo_error");
+}
+
GQuark
cr_repomd_error_quark(void)
{
const char *cr_strerror(cr_Error rc);
/* Error domains */
-#define CR_CMD_ERROR cr_cmd_error_quark()
#define CR_CHECKSUM_ERROR cr_checksum_error_quark()
+#define CR_CMD_ERROR cr_cmd_error_quark()
#define CR_COMPRESSION_WRAPPER_ERROR cr_compression_wrapper_error_quark()
#define CR_DB_ERROR cr_db_error_quark()
#define CR_LOAD_METADATA_ERROR cr_load_metadata_error_quark()
#define CR_LOCATE_METADATA_ERROR cr_locate_metadata_error_quark()
#define CR_MISC_ERROR cr_misc_error_quark()
+#define CR_MODIFYREPO_ERROR cr_modifyrepo_error_quark()
#define CR_PARSEPKG_ERROR cr_parsepkg_error_quark()
#define CR_REPOMD_ERROR cr_repomd_error_quark()
#define CR_REPOMD_RECORD_ERROR cr_repomd_record_error_quark()
#define CR_XML_PARSER_PRI_ERROR cr_xml_parser_pri_error_quark()
#define CR_XML_PARSER_REPOMD_ERROR cr_xml_parser_repomd_error_quark()
-GQuark cr_cmd_error_quark(void);
GQuark cr_checksum_error_quark(void);
+GQuark cr_cmd_error_quark(void);
GQuark cr_compression_wrapper_error_quark(void);
GQuark cr_db_error_quark(void);
GQuark cr_load_metadata_error_quark(void);
GQuark cr_locate_metadata_error_quark(void);
GQuark cr_misc_error_quark(void);
+GQuark cr_modifyrepo_error_quark(void);
GQuark cr_parsepkg_error_quark(void);
GQuark cr_repomd_error_quark(void);
GQuark cr_repomd_record_error_quark(void);
--- /dev/null
+/* 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 <glib.h>
+#include <glib/gstdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include "error.h"
+#include "version.h"
+#include "compression_wrapper.h"
+#include "misc.h"
+#include "locate_metadata.h"
+#include "load_metadata.h"
+#include "package.h"
+#include "repomd.h"
+#include "sqlite.h"
+#include "xml_file.h"
+#include "modifyrepo_shared.h"
+
+#define G_LOG_DOMAIN ((gchar*) 0)
+
+typedef struct {
+
+ gboolean version;
+ gchar *mdtype;
+ gchar *remove;
+ gboolean compress;
+ gboolean no_compress;
+ gchar *compress_type;
+ gchar *checksum;
+ gboolean unique_md_filenames;
+ gboolean simple_md_filenames;
+ gboolean verbose;
+ gchar *batchfile;
+ gchar *new_name;
+
+} RawCmdOptions;
+
+static gboolean
+parse_arguments(int *argc, char ***argv, RawCmdOptions *options, GError **err)
+{
+ const GOptionEntry cmd_entries[] = {
+
+ { "version", 0, 0, G_OPTION_ARG_NONE, &(options->version),
+ "Show program's version number and exit.", NULL },
+ { "mdtype", 0, 0, G_OPTION_ARG_STRING, &(options->mdtype),
+ "Specific datatype of the metadata, will be derived from "
+ "the filename if not specified.", "MDTYPE" },
+ { "remove", 0, 0, G_OPTION_ARG_STRING, &(options->remove),
+ "Remove specified file from repodata.", NULL },
+ { "compress", 0, 0, G_OPTION_ARG_NONE, &(options->compress),
+ "Compress the new repodata before adding it to the repo. "
+ "(default)", NULL },
+ { "no-compress", 0, 0, G_OPTION_ARG_NONE, &(options->no_compress),
+ "Do not compress the new repodata before adding it to the repo.",
+ NULL },
+ { "compress-type", 0, 0, G_OPTION_ARG_STRING, &(options->compress_type),
+ "Compression format to use.", NULL },
+ { "checksum", 's', 0, G_OPTION_ARG_STRING, &(options->checksum),
+ "Specify the checksum type to use. (default: sha256)", "SUMTYPE" },
+ { "unique-md-filenames", 0, 0, G_OPTION_ARG_NONE,
+ &(options->unique_md_filenames),
+ "Include the file's checksum in the filename, helps with proxies. "
+ "(default)", NULL },
+ { "simple-md-filenames", 0, 0, G_OPTION_ARG_NONE,
+ &(options->simple_md_filenames),
+ "Do not include the file's checksum in the filename.", NULL },
+ { "verbose", 0, 0, G_OPTION_ARG_NONE, &(options->verbose),
+ "Verbose output.", NULL},
+ { "batchfile", 'f', 0, G_OPTION_ARG_STRING, &(options->batchfile),
+ "Batch file.", "BATCHFILE" },
+ { "new-name", 0, 0, G_OPTION_ARG_STRING, &(options->new_name),
+ "New filename for the file", "NEWFILENAME"},
+ { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL },
+
+ };
+
+ // Frstly, set default values
+ options->version = FALSE;
+ options->mdtype = NULL;
+ options->remove = NULL;
+ options->compress = TRUE;
+ options->no_compress = FALSE;
+ options->compress_type = NULL;
+ options->checksum = NULL;
+ options->unique_md_filenames = TRUE;
+ options->simple_md_filenames = FALSE;
+ options->verbose = FALSE;
+ options->batchfile = NULL;
+ options->new_name = NULL;
+
+ GOptionContext *context;
+ context = g_option_context_new(": Modify a repository's repomd.xml");
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ gboolean ret = g_option_context_parse(context, argc, argv, err);
+ g_option_context_free(context);
+ return ret;
+}
+
+static gboolean
+check_arguments(RawCmdOptions *options, GError **err)
+{
+ // --no-compress
+ if (options->no_compress) {
+ options->compress = FALSE;
+ if (options->compress_type) {
+ g_warning("Use --compress-type simultaneously with --no-compress "
+ "doesn't make a sense");
+ }
+ }
+
+ // --compress-type
+ if (options->compress_type
+ && cr_compression_type(options->compress_type) == \
+ CR_CW_UNKNOWN_COMPRESSION)
+ {
+ g_set_error(err, CR_MODIFYREPO_ERROR, CRE_ERROR,
+ "Unknown compression type \"%s\"", options->compress_type);
+ return FALSE;
+ }
+
+ // -s/--checksum
+ if (options->checksum
+ && cr_checksum_type(options->checksum) == CR_CHECKSUM_UNKNOWN)
+ {
+ g_set_error(err, CR_MODIFYREPO_ERROR, CRE_ERROR,
+ "Unknown checksum type \"%s\"", options->checksum);
+ return FALSE;
+ }
+
+ // --unique_md_filenames && --simple_md_filenames
+ if (options->simple_md_filenames) {
+ options->unique_md_filenames = FALSE;
+ }
+
+ // -f/--batchfile
+ if (options->batchfile
+ && !g_file_test(options->batchfile, G_FILE_TEST_IS_REGULAR)) {
+ g_set_error(err, CR_MODIFYREPO_ERROR, CRE_ERROR,
+ "File \"%s\" doesn't exist", options->batchfile);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+print_usage(void)
+{
+ g_printerr(
+ "Usage: modifyrepo_c [options] <input metadata> <output repodata>\n"
+ "Usage: modifyrepo_c --remove <metadata type> <output repodata>\n"
+ "Usage: modifyrepo_c [options] --batchfile "
+ "<batch file> <output repodata>\n");
+}
+
+static gboolean
+cmd_options_to_task(GSList **modifyrepotasks,
+ RawCmdOptions *options,
+ gchar *metadatapath,
+ GError **err)
+{
+ assert(modifyrepotasks);
+ assert(!err || *err == NULL);
+
+ if (!options)
+ return TRUE;
+
+ //assert(metadatapath || options->remove);
+
+ if (options->remove)
+ g_debug("Preparing remove-task for: %s", options->remove);
+ else
+ g_debug("Preparing task for: %s", metadatapath);
+
+ if (metadatapath && !g_file_test(metadatapath, G_FILE_TEST_IS_REGULAR)) {
+ g_set_error(err, CR_MODIFYREPO_ERROR, CRE_ERROR,
+ "File \"%s\" is not regular file or doesn't exists",
+ metadatapath);
+ return FALSE;
+ }
+
+ if (options->remove)
+ metadatapath = options->remove;
+
+ cr_ModifyRepoTask *task = cr_modifyrepotask_new();
+ task->path = cr_safe_string_chunk_insert_null(task->chunk, metadatapath);
+ task->type = cr_safe_string_chunk_insert_null(task->chunk, options->mdtype);
+ task->remove = (options->remove) ? TRUE : FALSE;
+ task->compress = options->compress;
+ task->compress_type = cr_compression_type(options->compress_type);
+ task->unique_md_filenames = options->unique_md_filenames;
+ task->checksum_type = cr_checksum_type(options->checksum);
+ task->new_name = cr_safe_string_chunk_insert_null(task->chunk,
+ options->new_name);
+
+ *modifyrepotasks = g_slist_prepend(*modifyrepotasks, task);
+
+ g_debug("Task: [path: %s, type: %s, remove: %d, compress: %d, "
+ "compress_type: %d (%s), unique_md_filenames: %d, "
+ "checksum_type: %d (%s), new_name: %s]",
+ task->path, task->type, task->remove, task->compress,
+ task->compress_type, cr_compression_suffix(task->compress_type),
+ task->unique_md_filenames, task->checksum_type,
+ cr_checksum_name_str(task->checksum_type), task->new_name);
+
+ return TRUE;
+}
+
+int
+main(int argc, char **argv)
+{
+ gboolean ret = TRUE;
+ RawCmdOptions options;
+ GError *err = NULL;
+
+ // Parse arguments
+
+ parse_arguments(&argc, &argv, &options, &err);
+ if (err) {
+ g_printerr("%s\n", err->message);
+ print_usage();
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ // Set logging
+
+ g_log_set_default_handler(cr_log_fn, NULL);
+ if (options.verbose) {
+ // Verbose mode
+ GLogLevelFlags levels = G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_WARNING;
+ g_log_set_handler(NULL, levels, cr_log_fn, NULL);
+ g_log_set_handler("C_CREATEREPOLIB", levels, cr_log_fn, NULL);
+ } else {
+ // Standard mode
+ GLogLevelFlags levels = G_LOG_LEVEL_DEBUG;
+ g_log_set_handler(NULL, levels, cr_null_log_fn, NULL);
+ g_log_set_handler("C_CREATEREPOLIB", levels, cr_null_log_fn, NULL);
+ }
+
+ // Print version if required
+
+ if (options.version) {
+ printf("Version: %d.%d.%d\n", CR_VERSION_MAJOR,
+ CR_VERSION_MINOR,
+ CR_VERSION_PATCH);
+ exit(EXIT_SUCCESS);
+ }
+
+ // Check arguments
+
+ check_arguments(&options, &err);
+ if (err) {
+ g_printerr("%s\n", err->message);
+ print_usage();
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ // Prepare list of tasks to do
+
+ gchar *repodatadir = NULL;
+ GSList *modifyrepotasks = NULL;
+
+ if (!options.batchfile && !options.remove && argc == 3) {
+ // three arguments (prog, metadata, repodata_dir)
+ repodatadir = argv[2];
+ ret = cmd_options_to_task(&modifyrepotasks,
+ &options,
+ argv[1],
+ &err);
+ } else if (options.batchfile && argc == 2) {
+ // two arguments (prog, repodata_dir)
+ repodatadir = argv[1];
+ ret = cr_modifyrepo_parse_batchfile(options.batchfile,
+ &modifyrepotasks,
+ &err);
+ } else if (!options.batchfile && options.remove && argc == 2) {
+ // two arguments (prog, repodata_dir)
+ repodatadir = argv[1];
+ ret = cmd_options_to_task(&modifyrepotasks,
+ &options,
+ NULL,
+ &err);
+ } else {
+ // Bad arguments
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (!ret) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ // Process the tasks
+
+ ret = cr_modifyrepo(modifyrepotasks, repodatadir, &err);
+ g_slist_free_full(modifyrepotasks, (GDestroyNotify)cr_modifyrepotask_free);
+
+ if (!ret) {
+ g_printerr("%s\n", err->message);
+ g_error_free(err);
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
--- /dev/null
+/* 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 <glib.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include "error.h"
+#include "logging.h"
+#include "misc.h"
+#include "checksum.h"
+#include "modifyrepo_shared.h"
+#include "compression_wrapper.h"
+#include "threads.h"
+#include "xml_dump.h"
+
+#define DEFAULT_COMPRESSION CR_CW_GZ_COMPRESSION
+#define DEFAULT_CHECKSUM CR_CHECKSUM_SHA256
+
+cr_ModifyRepoTask *
+cr_modifyrepotask_new(void)
+{
+ cr_ModifyRepoTask *task = g_new0(cr_ModifyRepoTask, 1);
+ task->chunk = g_string_chunk_new(16);
+ return task;
+}
+
+void
+cr_modifyrepotask_free(cr_ModifyRepoTask *task)
+{
+ if (!task) return;
+ g_string_chunk_free(task->chunk);
+ g_free(task);
+}
+
+gboolean
+cr_modifyrepo(GSList *modifyrepotasks, gchar *repopath, GError **err)
+{
+ assert(!err || *err == NULL);
+
+ if (!modifyrepotasks) {
+ g_debug("%s: No tasks to process", __func__);
+ return TRUE;
+ }
+
+ // Parse repomd.xml
+
+ gchar *repomd_path = g_build_filename(repopath, "repomd.xml", NULL);
+ if (!g_file_test(repomd_path, G_FILE_TEST_IS_REGULAR)) {
+ g_set_error(err, CR_MODIFYREPO_ERROR, CRE_IO,
+ "Regular file \"%s\" doesn't exists", repomd_path);
+ g_free(repomd_path);
+ return FALSE;
+ }
+
+ cr_Repomd *repomd = cr_repomd_new();
+ int rc = cr_xml_parse_repomd(repomd_path, repomd, cr_warning_cb,
+ "Repomd XML parser", err);
+ if (rc != CRE_OK) {
+ g_debug("%s: Error while parsing repomd.xml", __func__);
+ cr_repomd_free(repomd);
+ g_free(repomd_path);
+ return FALSE;
+ }
+
+ // TODO:
+ // (?) Autodetect used checksum_type
+ // (?) Autodetect if unique_md_filenames are used
+
+ // Prepare tasks
+
+ for (GSList *elem = modifyrepotasks; elem; elem = g_slist_next(elem)) {
+ cr_ModifyRepoTask *task = elem->data;
+
+ if (!task->type) {
+ // If type is not specified, derive it from path or new name
+ gchar *basename;
+
+ if (task->new_name)
+ basename = g_path_get_basename(task->new_name);
+ else
+ basename = g_path_get_basename(task->path);
+
+ // Split at first '.' in filename and use only first part
+ for (gchar *tmp=basename; *tmp; tmp++) {
+ if (*tmp == '.') {
+ *tmp = '\0';
+ break;
+ }
+ }
+
+ task->type = cr_safe_string_chunk_insert_null(task->chunk,
+ basename);
+ g_debug("%s: Use derived type \"%s\" (%s)",
+ __func__, task->type, basename);
+ g_free(basename);
+ }
+
+ if (task->remove)
+ continue;
+
+ if (task->compress && task->compress_type == CR_CW_UNKNOWN_COMPRESSION)
+ // If compression enabled but type not specified, use default
+ task->compress_type = DEFAULT_COMPRESSION;
+
+ if (task->checksum_type == CR_CHECKSUM_UNKNOWN)
+ // If no checksum type specified, use default
+ task->checksum_type = DEFAULT_CHECKSUM;
+ }
+
+ // Check tasks
+
+ for (GSList *elem = modifyrepotasks; elem; elem = g_slist_next(elem)) {
+ cr_ModifyRepoTask *task = elem->data;
+
+ if (task->remove) {
+
+ // Check if metadata of a type that should be removed
+ // exists in repomd
+ if (!cr_repomd_get_record(repomd, task->type))
+ g_warning("Record of type \"%s\", which should be removed, "
+ "doesn't exist in repomd.xml", task->path);
+
+ if (task->new_name)
+ g_warning("Use remove with new_name doesn't make a sense");
+
+ } else {
+
+ // Check if file exists
+ if (!g_file_test(task->path, G_FILE_TEST_IS_REGULAR)) {
+ g_debug("%s: Regular file \"%s\" doesn't exist",
+ __func__, task->path);
+ cr_repomd_free(repomd);
+ g_free(repomd_path);
+ return FALSE;
+ }
+
+ // Check if new_name is not empty string
+ if (task->new_name) {
+ if (!g_strcmp0(task->new_name, "")) {
+ g_debug("%s: New name cannot be empty", __func__);
+ cr_repomd_free(repomd);
+ g_free(repomd_path);
+ return FALSE;
+ }
+ }
+
+ // Check if record with this name doesn't exists yet
+ if (cr_repomd_get_record(repomd, task->type))
+ g_warning("Record with type \"%s\" already exists "
+ "in repomd.xml", task->type);
+
+ }
+ }
+
+ //
+ // Modifications of the target repository starts here
+ //
+
+ // Add (copy) new metadata to repodata/ directory
+ for (GSList *elem = modifyrepotasks; elem; elem = g_slist_next(elem)) {
+ cr_ModifyRepoTask *task = elem->data;
+
+ if (task->remove)
+ // Skip removing task
+ continue;
+
+ gchar *src_fn = task->path;
+ gchar *dst_fn = NULL;
+ const gchar *suffix = NULL;
+ cr_CompressionType compress_type = CR_CW_NO_COMPRESSION;
+
+ if (task->compress) {
+ compress_type = task->compress_type;
+ suffix = cr_compression_suffix(compress_type);
+ }
+
+ // Prepare dst filename
+ gchar *filename = NULL;
+ if (task->new_name)
+ filename = g_path_get_basename(task->new_name);
+ else
+ filename = g_path_get_basename(src_fn);
+
+ if (suffix) {
+ gchar *tmp_fn = g_strconcat(filename, suffix, NULL);
+ g_free(filename);
+ filename = tmp_fn;
+ }
+
+ dst_fn = g_build_filename(repopath, filename, NULL);
+ g_free(filename);
+
+ if (g_file_test(dst_fn, G_FILE_TEST_EXISTS)) {
+ g_warning("Destination file \"%s\" already exists and will be "
+ "overwritten", dst_fn);
+ }
+
+ // Do the copy
+ g_debug("%s: Copy & compress operation %s -> %s",
+ __func__, src_fn, dst_fn);
+
+ if (cr_compress_file(src_fn, dst_fn, compress_type, err) != CRE_OK) {
+ g_debug("%s: Copy & compress operation failed", __func__);
+ cr_repomd_free(repomd);
+ g_free(repomd_path);
+ g_free(dst_fn);
+ return FALSE;
+ }
+
+ task->repopath = cr_safe_string_chunk_insert_null(task->chunk, dst_fn);
+ g_free(dst_fn);
+ }
+
+ // Prepare new repomd records
+ GSList *repomdrecords = NULL;
+ GSList *repomdrecords_uniquefn = NULL;
+ GSList *repomdrecordfilltasks = NULL;
+
+ GThreadPool *fill_pool = g_thread_pool_new(cr_repomd_record_fill_thread,
+ NULL, 5, FALSE, NULL);
+
+ for (GSList *elem = modifyrepotasks; elem; elem = g_slist_next(elem)) {
+ cr_ModifyRepoTask *task = elem->data;
+
+ if (task->remove)
+ continue;
+
+ cr_RepomdRecord *rec = cr_repomd_record_new(task->type,
+ task->repopath);
+ cr_RepomdRecordFillTask *filltask = cr_repomdrecordfilltask_new(rec,
+ task->checksum_type, NULL);
+ g_thread_pool_push(fill_pool, filltask, NULL);
+
+ repomdrecords = g_slist_prepend(repomdrecords, rec);
+ if (task->unique_md_filenames)
+ repomdrecords_uniquefn = g_slist_prepend(repomdrecords_uniquefn, rec);
+ repomdrecordfilltasks = g_slist_prepend(repomdrecordfilltasks,
+ filltask);
+ }
+
+ g_thread_pool_free(fill_pool, FALSE, TRUE); // Wait
+
+ for (GSList *elem = repomdrecordfilltasks; elem; elem = g_slist_next(elem)) {
+ // Clean up tasks
+ cr_RepomdRecordFillTask *filltask = elem->data;
+ cr_repomdrecordfilltask_free(filltask, NULL);
+ }
+ g_slist_free(repomdrecordfilltasks);
+
+ // Detach records from repomd
+ GSList *recordstoremove = NULL;
+ for (GSList *elem = modifyrepotasks; elem; elem = g_slist_next(elem)) {
+ cr_ModifyRepoTask *task = elem->data;
+
+ // Remove both, records that will be removed but also
+ // records with types that will be added.
+ cr_RepomdRecord *rec = cr_repomd_get_record(repomd, task->type);
+ if (rec) {
+ g_debug("%s: Removing record \"%s\" from repomd.xml",
+ __func__, task->type);
+ recordstoremove = g_slist_prepend(recordstoremove, rec);
+ cr_repomd_detach_record(repomd, rec);
+ }
+ }
+
+ // Prepend checksum
+ for (GSList *elem = repomdrecords_uniquefn;
+ elem;
+ elem = g_slist_next(elem))
+ {
+ cr_RepomdRecord *rec = elem->data;
+ cr_repomd_record_rename_file(rec, NULL);
+ }
+ g_slist_free(repomdrecords_uniquefn);
+
+ // Add records into repomd
+ for (GSList *elem = repomdrecords; elem; elem = g_slist_next(elem)) {
+ cr_RepomdRecord *rec = elem->data;
+ cr_repomd_set_record(repomd, rec);
+ }
+ g_slist_free(repomdrecords);
+
+ // Write repomd.xml
+ gchar *repomd_xml = cr_xml_dump_repomd(repomd, NULL);
+ printf("REPOMD.XML:\n%s", repomd_xml);
+
+ g_debug("%s: Writing modified %s", __func__, repomd_path);
+ gboolean ret = cr_write_to_file(err, repomd_path, "%s", repomd_xml);
+
+ g_free(repomd_xml);
+ g_free(repomd_path);
+
+ if (!ret) {
+ assert(!err || *err);
+ cr_repomd_free(repomd);
+ return FALSE;
+ }
+
+ // Delete files of removed records
+ for (GSList *elem = recordstoremove; elem; elem = g_slist_next(elem)) {
+ cr_RepomdRecord *rec = elem->data;
+
+ if (rec->location_base)
+ // Do not even try to remove records with base location
+ continue;
+
+ // Firstly check if the file that should be deleted isn't
+ // really used by other record anymore.
+ // It could happend if user add a file, that already exists,
+ // in repodata. Then we don't want to remove this file.
+ gboolean remove_this = TRUE;
+ for (GSList *e = repomd->records; e; e = g_slist_next(e)) {
+ cr_RepomdRecord *lrec = e->data;
+
+ if (!g_strcmp0(rec->location_href, lrec->location_href)) {
+ remove_this = FALSE;
+ break;
+ }
+ }
+
+ if (!remove_this)
+ break;
+
+ gchar *realpath = g_build_filename(repopath,
+ "../",
+ rec->location_href,
+ NULL);
+
+ g_debug("%s: Removing \"%s\"", __func__, realpath);
+
+ if (remove(realpath) == -1)
+ g_warning("Cannot remove \"%s\": %s", realpath, strerror(errno));
+ g_free(realpath);
+ }
+ g_slist_free_full(recordstoremove, (GDestroyNotify)cr_repomd_record_free);
+
+ cr_repomd_free(repomd);
+
+ return TRUE;
+}
+
+gboolean
+cr_modifyrepo_parse_batchfile(const gchar *path,
+ GSList **modifyrepotasks,
+ GError **err)
+{
+ assert(!err || *err == NULL);
+
+ if (!path)
+ return TRUE;
+
+ GKeyFile *keyfile = g_key_file_new();
+
+ gboolean ret = TRUE;
+ ret = g_key_file_load_from_file(keyfile, path, G_KEY_FILE_NONE, err);
+ if (!ret) {
+ g_debug("%s: Parsing of modifyrepo batchfile failed", __func__);
+ return FALSE;
+ }
+
+ gsize length;
+ gchar **groups = g_key_file_get_groups(keyfile, &length);
+ GSList *tasks = NULL;
+ gboolean success = TRUE;
+ for (gsize x = 0; x < length; x++) {
+ gchar *group = groups[x];
+ assert(group);
+
+ g_debug("%s: Group: %s", __func__, group);
+
+ cr_ModifyRepoTask *task = cr_modifyrepotask_new();
+ tasks = g_slist_append(tasks, task);
+
+ gchar *tmp_str;
+
+ task->path = cr_safe_string_chunk_insert(task->chunk, group);
+ task->type = cr_safe_string_chunk_insert_and_free(task->chunk,
+ g_key_file_get_string(keyfile, group, "type", NULL));
+ task->remove = cr_key_file_get_boolean_default(keyfile, group,
+ "remove", FALSE, NULL);
+ task->compress = cr_key_file_get_boolean_default(keyfile, group,
+ "compress", TRUE, NULL);
+ tmp_str = g_key_file_get_string(keyfile, group, "compress-type", NULL);
+ task->compress_type = cr_compression_type(tmp_str);
+ g_free(tmp_str);
+ task->unique_md_filenames = cr_key_file_get_boolean_default(keyfile,
+ group, "unique-md-filenames", TRUE, NULL);
+ tmp_str = g_key_file_get_string(keyfile, group, "checksum", NULL);
+ task->checksum_type = cr_checksum_type(tmp_str);
+ g_free(tmp_str);
+ task->new_name = cr_safe_string_chunk_insert_and_free(task->chunk,
+ g_key_file_get_string(keyfile, group, "new-name", NULL));
+
+ g_debug("Task: [path: %s, type: %s, remove: %d, compress: %d, "
+ "compress_type: %d (%s), unique_md_filenames: %d, "
+ "checksum_type: %d (%s), new_name: %s]",
+ task->path, task->type, task->remove, task->compress,
+ task->compress_type, cr_compression_suffix(task->compress_type),
+ task->unique_md_filenames, task->checksum_type,
+ cr_checksum_name_str(task->checksum_type), task->new_name);
+
+ }
+
+ g_strfreev(groups);
+
+ if (success) {
+ *modifyrepotasks = g_slist_concat(*modifyrepotasks, tasks);
+ } else {
+ g_slist_free_full(tasks, (GDestroyNotify)cr_modifyrepotask_free);
+ }
+
+ g_key_file_free(keyfile);
+ return success;
+}
--- /dev/null
+/* 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.
+ */
+
+#ifndef __C_CREATEREPOLIB_MODIFYREPO_SHARED_H__
+#define __C_CREATEREPOLIB_MODIFYREPO_SHARED_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "checksum.h"
+#include "compression_wrapper.h"
+#include "package.h"
+
+/** \defgroup modifyrepo_shared Modifyrepo API.
+ *
+ * Module with modifyrepo API
+ *
+ * \addtogroup modifyrepo_shared
+ * @{
+ */
+
+typedef struct {
+
+ gchar *path;
+ gchar *type;
+ gboolean remove;
+ gboolean compress;
+ cr_CompressionType compress_type;
+ gboolean unique_md_filenames;
+ cr_ChecksumType checksum_type;
+ gchar *new_name;
+
+ // Internal use
+ gchar *repopath;
+ GStringChunk *chunk;
+
+} cr_ModifyRepoTask;
+
+cr_ModifyRepoTask *
+cr_modifyrepotask_new(void);
+
+void
+cr_modifyrepotask_free(cr_ModifyRepoTask *task);
+
+gboolean
+cr_modifyrepo(GSList *modifyrepotasks, gchar *repopath, GError **err);
+
+gboolean
+cr_modifyrepo_parse_batchfile(const gchar *path,
+ GSList **modifyrepotasks,
+ GError **err);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __C_CREATEREPOLIB_MODIFYREPO_SHARED__ */