/
niceSOFT
/
createrepo_c
Обзор
Документация
Войти
/
niceSOFT
/
createrepo_c
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/cmd_parser.c
711 строк
30 KB
Shreenidhi Shedi
Properly guard code with CR_DELTA_RPM_SUPPORT in missed out places
19 фев 2026, 21:28
19 фев 2026, 21:28
b27beab
Код
Авторство
О чём код?
/* createrepo_c - Library of routines for manipulation with repodata * Copyright (C) 2012 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 <string.h> #include <assert.h> #include <errno.h> #include <locale.h> #include "cmd_parser.h" #include "deltarpms.h" #include "error.h" #include "compression_wrapper.h" #include "misc.h" #include "cleanup.h" #define ERR_DOMAIN CREATEREPO_C_ERROR #define DEFAULT_CHECKSUM "sha256" #define DEFAULT_WORKERS 5 #define DEFAULT_UNIQUE_MD_FILENAMES TRUE #define DEFAULT_IGNORE_LOCK FALSE #define DEFAULT_LOCAL_SQLITE FALSE #define DEFAULT_FORMAT_PRETTY TRUE struct CmdOptions _cmd_options = { .changelog_limit = DEFAULT_CHANGELOG_LIMIT, .checksum = NULL, .workers = DEFAULT_WORKERS, .unique_md_filenames = DEFAULT_UNIQUE_MD_FILENAMES, .checksum_type = CR_CHECKSUM_SHA256, .retain_old = 0, .compression_type = CR_CW_UNKNOWN_COMPRESSION, .general_compression_type = CR_CW_UNKNOWN_COMPRESSION, .ignore_lock = DEFAULT_IGNORE_LOCK, .md_max_age = G_GINT64_CONSTANT(0), .cachedir = NULL, .local_sqlite = DEFAULT_LOCAL_SQLITE, .cut_dirs = 0, .location_prefix = NULL, .repomd_checksum = NULL, .pretty = DEFAULT_FORMAT_PRETTY, #ifdef CR_DELTA_RPM_SUPPORT .deltas = FALSE, .oldpackagedirs = NULL, .num_deltas = 1, .max_delta_rpm_size = CR_DEFAULT_MAX_DELTA_RPM_SIZE, #endif .checksum_cachedir = NULL, .repomd_checksum_type = CR_CHECKSUM_SHA256, .zck_compression = FALSE, .zck_dict_dir = NULL, .recycle_pkglist = FALSE, .keep_all_metadata = TRUE, .nevra_duplicates = CR_ARG_DUP_NEVRA_KEEP_ALL, }; gboolean duplicated_nevra_option_parser(const gchar *option_name, const gchar *value, gpointer data, GError **error) { if (!g_strcmp0(value, "keep")) _cmd_options.nevra_duplicates = CR_ARG_DUP_NEVRA_KEEP_ALL; else if (!g_strcmp0(value, "keep-last")) _cmd_options.nevra_duplicates = CR_ARG_DUP_NEVRA_KEEP_LAST; else { g_set_error(error, ERR_DOMAIN, CRE_BADARG, "Bad --duplicated-nevra argument, use 'keep' or 'keep-last'."); return FALSE; } return TRUE; } // Command line params static GOptionEntry cmd_entries[] = { { "version", 'V', 0, G_OPTION_ARG_NONE, &(_cmd_options.version), "Show program's version number and exit.", NULL}, { "quiet", 'q', 0, G_OPTION_ARG_NONE, &(_cmd_options.quiet), "Run quietly.", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &(_cmd_options.verbose), "Run verbosely.", NULL }, { "excludes", 'x', 0, G_OPTION_ARG_FILENAME_ARRAY, &(_cmd_options.excludes), "Path patterns to exclude, can be specified multiple times.", "PACKAGE_NAME_GLOB" }, { "basedir", 0, 0, G_OPTION_ARG_FILENAME, &(_cmd_options.basedir), "Basedir for path to directories.", "BASEDIR" }, { "baseurl", 'u', 0, G_OPTION_ARG_FILENAME, &(_cmd_options.location_base), "Optional base URL location for all files.", "URL" }, { "groupfile", 'g', 0, G_OPTION_ARG_FILENAME, &(_cmd_options.groupfile), "Path to groupfile to include in metadata.", "GROUPFILE" }, { "checksum", 's', 0, G_OPTION_ARG_STRING, &(_cmd_options.checksum), "Choose the checksum type used in repomd.xml and for packages in the " "metadata. The default is now \"sha256\".", "CHECKSUM_TYPE" }, { "pretty", 'p', 0, G_OPTION_ARG_NONE, &(_cmd_options.pretty), "Make sure all xml generated is formatted (default)", NULL }, { "no-pretty", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &(_cmd_options.pretty), "No extra indentation in generated xml", NULL }, { "database", 'd', 0, G_OPTION_ARG_NONE, &(_cmd_options.database), "DEPRECATED: Generate sqlite databases for use with yum.", NULL }, { "no-database", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.no_database), "Do not generate sqlite databases in the repository (default).", NULL }, { "filelists-ext", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.filelists_ext), "Create filelists-ext metadata with file hashes.", NULL }, { "update", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.update), "If metadata already exists in the outputdir and an rpm is unchanged " "(based on file size and mtime) since the metadata was generated, reuse " "the existing metadata rather than recalculating it. In the case of a " "large repository with only a few new or modified rpms " "this can significantly reduce I/O and processing time.", NULL }, { "update-md-path", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &(_cmd_options.update_md_paths), "Existing metadata from this path are loaded and reused in addition to those " "present in the outputdir (works only with --update). Can be specified multiple times.", NULL }, { "skip-stat", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.skip_stat), "Skip the stat() call on a --update, assumes if the filename is the same " "then the file is still the same (only use this if you're fairly " "trusting or gullible).", NULL }, { "split", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.split), "Run in split media mode. Rather than pass a single directory, take a set of" "directories corresponding to different volumes in a media set. " "Meta data is created in the first given directory", NULL }, { "pkglist", 'i', 0, G_OPTION_ARG_FILENAME, &(_cmd_options.pkglist), "Specify a text file which contains the complete list of files to " "include in the repository from the set found in the directory. File " "format is one package per line, no wildcards or globs.", "FILENAME" }, { "includepkg", 'n', 0, G_OPTION_ARG_FILENAME_ARRAY, &(_cmd_options.includepkg), "Specify pkgs to include on the command line. Takes urls as well as local paths.", "PACKAGE" }, { "outputdir", 'o', 0, G_OPTION_ARG_FILENAME, &(_cmd_options.outputdir), "Optional output directory.", "URL" }, { "skip-symlinks", 'S', 0, G_OPTION_ARG_NONE, &(_cmd_options.skip_symlinks), "Ignore symlinks of packages.", NULL}, { "changelog-limit", 0, 0, G_OPTION_ARG_INT, &(_cmd_options.changelog_limit), "Only import the last N changelog entries, from each rpm, into the metadata.", "NUM" }, { "unique-md-filenames", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.unique_md_filenames), "Include the file's checksum in the metadata filename, helps HTTP caching (default).", NULL }, { "simple-md-filenames", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.simple_md_filenames), "Do not include the file's checksum in the metadata filename.", NULL }, { "retain-old-md", 0, 0, G_OPTION_ARG_INT, &(_cmd_options.retain_old), "Specify NUM to 0 to remove all repodata present in old repomd.xml or any other positive number to keep all old repodata. " "Use --compatibility flag to get the behavior of original createrepo: " "Keep around the latest (by timestamp) NUM copies of the old repodata (works only for primary, filelists, other and their DB variants).", "NUM" }, { "distro", 0, 0, G_OPTION_ARG_STRING_ARRAY, &(_cmd_options.distro_tags), "Distro tag and optional cpeid: --distro'cpeid,textname'.", "DISTRO" }, { "content", 0, 0, G_OPTION_ARG_STRING_ARRAY, &(_cmd_options.content_tags), "Tags for the content in the repository.", "CONTENT_TAGS" }, { "repo", 0, 0, G_OPTION_ARG_STRING_ARRAY, &(_cmd_options.repo_tags), "Tags to describe the repository itself.", "REPO_TAGS" }, { "revision", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.revision), "User-specified revision for this repository.", "REVISION" }, { "set-timestamp-to-revision", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.set_timestamp_to_revision), "Set timestamp fields in repomd.xml and last modification times of created repodata to a value given with --revision. " "This requires --revision to be a timestamp formatted in 'date +%s' format.", NULL }, { "read-pkgs-list", 0, 0, G_OPTION_ARG_FILENAME, &(_cmd_options.read_pkgs_list), "Output the paths to the pkgs actually read useful with --update.", "READ_PKGS_LIST" }, { "workers", 0, 0, G_OPTION_ARG_INT, &(_cmd_options.workers), "Number of workers to spawn to read rpms.", NULL }, { "xz", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.xz_compression), "Use xz for repodata compression.", NULL }, #ifdef WITH_ZCHUNK { "compress-type", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.compress_type), "Which compression type to use for additional metadata files (comps, updateinfo, etc). Supported values are: bz2, gz, zck, zstd, xz.", "COMPRESSION_TYPE" }, { "general-compress-type", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.general_compress_type), "Which compression type to use (even for primary, filelists and other xml). Supported values are: bz2, gz, zck, zstd, xz.", "COMPRESSION_TYPE" }, { "zck", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.zck_compression), "Generate zchunk files as well as the standard repodata.", NULL }, { "zck-dict-dir", 0, 0, G_OPTION_ARG_FILENAME, &(_cmd_options.zck_dict_dir), "Directory containing compression dictionaries for use by zchunk", "ZCK_DICT_DIR" }, #else { "compress-type", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.compress_type), "Which compression type to use for additional metadata files (comps, updateinfo, etc). Supported values are: bz2, gz, zstd, xz.", "COMPRESSION_TYPE" }, { "general-compress-type", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.general_compress_type), "Which compression type to use (even for primary, filelists and other xml). Supported values are: bz2, gz, zstd, xz.", "COMPRESSION_TYPE" }, #endif { "keep-all-metadata", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.keep_all_metadata), "Keep all additional metadata (not primary, filelists and other xml or sqlite files, " "nor their compressed variants) from source repository during update (default).", NULL }, { "discard-additional-metadata", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.discard_additional_metadata), "Discard all additional metadata (not primary, filelists and other xml or sqlite files, " "nor their compressed variants) from source repository during update.", NULL }, { "compatibility", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.compatibility), "Enforce maximal compatibility with classical createrepo and yum (Changes --retain-old-md behavior, defaults to Gzip for compression, produces sqlite metadata by default, leaves group metadata uncompressed).", NULL }, { "retain-old-md-by-age", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.retain_old_md_by_age), "During --update, remove all files in repodata/ which are older " "then the specified period of time. (e.g. '2h', '30d', ...). " "Available units (m - minutes, h - hours, d - days)", "AGE" }, { "cachedir", 'c', 0, G_OPTION_ARG_FILENAME, &(_cmd_options.cachedir), "Set path to cache dir", "CACHEDIR." }, #ifdef CR_DELTA_RPM_SUPPORT { "deltas", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.deltas), "Tells createrepo to generate deltarpms and the delta metadata.", NULL }, { "oldpackagedirs", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &(_cmd_options.oldpackagedirs), "Paths to look for older pkgs to delta against. Can be specified " "multiple times.", "PATH" }, { "num-deltas", 0, 0, G_OPTION_ARG_INT, &(_cmd_options.num_deltas), "The number of older versions to make deltas against. Defaults to 1.", "INT" }, { "max-delta-rpm-size", 0, 0, G_OPTION_ARG_INT64, &(_cmd_options.max_delta_rpm_size), "Max size of an rpm that to run deltarpm against (in bytes).", "MAX_DELTA_RPM_SIZE" }, #endif { "local-sqlite", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.local_sqlite), "Gen sqlite DBs locally (into a directory for temporary files). " "Sometimes, sqlite has a trouble to gen DBs on a NFS mount, " "use this option in such cases. " "This option could lead to a higher memory consumption " "if TMPDIR is set to /tmp or not set at all, because then the /tmp is " "used and /tmp dir is often a ramdisk.", NULL }, { "cut-dirs", 0, 0, G_OPTION_ARG_INT, &(_cmd_options.cut_dirs), "Ignore NUM of directory components in location_href during repodata " "generation", "NUM" }, { "location-prefix", 0, 0, G_OPTION_ARG_FILENAME, &(_cmd_options.location_prefix), "Append this prefix before location_href in output repodata", "PREFIX" }, { "repomd-checksum", 0, 0, G_OPTION_ARG_STRING, &(_cmd_options.repomd_checksum), "Checksum type to be used in repomd.xml", "CHECKSUM_TYPE"}, { "error-exit-val", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.error_exit_val), "Exit with retval 2 if there were any errors during processing (option deprecated, on by default)", NULL }, { "recycle-pkglist", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.recycle_pkglist), "Read the list of packages from old metadata directory and re-use it. This " "option is only useful with --update (complements --pkglist and friends).", NULL }, { "duplicated-nevra", 0, 0, G_OPTION_ARG_CALLBACK, duplicated_nevra_option_parser, "What to do about duplicates.", NULL, }, { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }, }; static GOptionEntry expert_entries[] = { { "ignore-lock", 0, 0, G_OPTION_ARG_NONE, &(_cmd_options.ignore_lock), "Expert (risky) option: Ignore an existing .repodata/. " "(Remove the existing .repodata/ and create an empty new one " "to serve as a lock for other createrepo instances. For the repodata " "generation, a different temporary dir with the name in format " "\".repodata.time.microseconds.pid/\" will be used). " "NOTE: Use this option on your " "own risk! If two createrepos run simultaneously, then the state of the " "generated metadata is not guaranteed - it can be inconsistent and wrong.", NULL }, { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }, }; struct CmdOptions *parse_arguments(int *argc, char ***argv, GError **err) { gboolean ret; GOptionContext *context; GOptionGroup *group_expert; assert(!err || *err == NULL); context = g_option_context_new("<directory_to_index>"); g_option_context_set_summary(context, "Program that creates a repomd " "(xml-based rpm metadata) repository from a set of rpms."); g_option_context_add_main_entries(context, cmd_entries, NULL); group_expert = g_option_group_new("expert", "Expert (risky) options", "Expert (risky) options", NULL, NULL); g_option_group_add_entries(group_expert, expert_entries); g_option_context_add_group(context, group_expert); /* setlocale is required to ensure that NON-ASCII chars can be parsed SEE https://developer-old.gnome.org/glib/unstable/glib-running.html#setlocale for further information */ setlocale(LC_ALL, ""); ret = g_option_context_parse(context, argc, argv, err); g_option_context_free(context); if (!ret) return NULL; return &(_cmd_options); } /** Convert string to compression type set an error if failed. * @param type_str String with compression type (e.g. "gz") * @param type Pointer to cr_CompressionType variable * @param err Err that will be set in case of error */ static gboolean check_and_set_compression_type(const char *type_str, cr_CompressionType *type, GError **err) { assert(!err || *err == NULL); _cleanup_string_free_ GString *compress_str = NULL; compress_str = g_string_ascii_down(g_string_new(type_str)); if (!strcmp(compress_str->str, "gz")) { *type = CR_CW_GZ_COMPRESSION; } else if (!strcmp(compress_str->str, "bz2")) { *type = CR_CW_BZ2_COMPRESSION; } else if (!strcmp(compress_str->str, "xz")) { *type = CR_CW_XZ_COMPRESSION; #ifdef WITH_ZCHUNK } else if (!strcmp(compress_str->str, "zck")) { *type = CR_CW_ZCK_COMPRESSION; #endif } else if (!strcmp(compress_str->str, "zstd")) { *type = CR_CW_ZSTD_COMPRESSION; } else { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Unknown/Unsupported compression type \"%s\"", type_str); return FALSE; } return TRUE; } /** Convert a time period to seconds (gint64 value) * Format: "[0-9]+[mhd]?" * Units: m - minutes, h - hours, d - days, ... * @param timeperiod Time period * @param time Time period converted to gint64 will be stored here */ static gboolean parse_period_of_time(const gchar *timeperiod, gint64 *time, GError **err) { assert(!err || *err == NULL); gchar *endptr = NULL; gint64 val = g_ascii_strtoll(timeperiod, &endptr, 0); *time = G_GINT64_CONSTANT(0); // Check the state of the conversion if (val == 0 && endptr == timeperiod) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Bad time period \"%s\"", timeperiod); return FALSE; } if (val == G_MAXINT64) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Time period \"%s\" is too high", timeperiod); return FALSE; } if (val == G_MININT64) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Time period \"%s\" is too low", timeperiod); return FALSE; } if (!endptr || endptr[0] == '\0') // Secs *time = (gint64) val; else if (!strcmp(endptr, "m")) // Minutes *time = (gint64) val*60; else if (!strcmp(endptr, "h")) // Hours *time = (gint64) val*60*60; else if (!strcmp(endptr, "d")) // Days *time = (gint64) val*24*60*60; else { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Bad time unit \"%s\"", endptr); return FALSE; } return TRUE; } gboolean check_arguments(struct CmdOptions *options, const char *input_dir, GError **err) { assert(!err || *err == NULL); // Check outputdir if (options->outputdir && !g_file_test(options->outputdir, G_FILE_TEST_EXISTS|G_FILE_TEST_IS_DIR)) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Specified outputdir \"%s\" doesn't exist", options->outputdir); return FALSE; } // Check workers if ((options->workers < 1) || (options->workers > 100)) { g_warning("Wrong number of workers - Using 5 workers."); options->workers = DEFAULT_WORKERS; } // Check changelog_limit if ((options->changelog_limit < -1)) { g_warning("Wrong changelog limit \"%d\" - Using 10", options->changelog_limit); options->changelog_limit = DEFAULT_CHANGELOG_LIMIT; } // Check simple filenames if (options->simple_md_filenames) { options->unique_md_filenames = FALSE; } // Check and set checksum type if (options->checksum) { cr_ChecksumType type; type = cr_checksum_type(options->checksum); if (type == CR_CHECKSUM_UNKNOWN) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Unknown/Unsupported checksum type \"%s\"", options->checksum); return FALSE; } options->checksum_type = type; } // Check and set checksum type for repomd if (options->repomd_checksum) { cr_ChecksumType type; type = cr_checksum_type(options->repomd_checksum); if (type == CR_CHECKSUM_UNKNOWN) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Unknown/Unsupported checksum type \"%s\"", options->repomd_checksum); return FALSE; } options->repomd_checksum_type = type; } else { options->repomd_checksum_type = options->checksum_type; } // Check and set compression type if (options->compress_type) { if (!check_and_set_compression_type(options->compress_type, &(options->compression_type), err)) { return FALSE; } } //options --xz has priority over compress_type, but not over general_compress_type if (options->xz_compression) { options->compression_type = CR_CW_XZ_COMPRESSION; } // Check and set general compression type if (options->general_compress_type) { if (!check_and_set_compression_type(options->general_compress_type, &(options->general_compression_type), err)) { return FALSE; } } int x; // Process exclude glob masks x = 0; while (options->excludes && options->excludes[x] != NULL) { GPatternSpec *pattern = g_pattern_spec_new(options->excludes[x]); options->exclude_masks = g_slist_prepend(options->exclude_masks, (gpointer) pattern); x++; } // Process includepkgs x = 0; while (options->includepkg && options->includepkg[x] != NULL) { options->include_pkgs = g_slist_prepend(options->include_pkgs, (gpointer) g_strdup(options->includepkg[x])); x++; } // Check groupfile options->groupfile_fullpath = NULL; if (options->groupfile) { gboolean remote = FALSE; if (g_str_has_prefix(options->groupfile, "/")) { // Absolute local path options->groupfile_fullpath = g_strdup(options->groupfile); } else if (strstr(options->groupfile, "://")) { // Remote groupfile remote = TRUE; options->groupfile_fullpath = g_strdup(options->groupfile); } else { // Relative path (from intput_dir) options->groupfile_fullpath = g_strconcat(input_dir, options->groupfile, NULL); } if (!remote && !g_file_test(options->groupfile_fullpath, G_FILE_TEST_IS_REGULAR)) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "groupfile %s doesn't exist", options->groupfile_fullpath); return FALSE; } } // Process pkglist file if (options->pkglist) { if (!g_file_test(options->pkglist, G_FILE_TEST_IS_REGULAR)) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "pkglist file \"%s\" doesn't exist", options->pkglist); return FALSE; } else { char *content = NULL; GError *tmp_err = NULL; if (!g_file_get_contents(options->pkglist, &content, NULL, &tmp_err)) { g_warning("Error while reading pkglist file: %s", tmp_err->message); g_error_free(tmp_err); g_free(content); } else { x = 0; char **pkgs = g_strsplit(content, "\n", 0); while (pkgs && pkgs[x] != NULL) { if (strlen(pkgs[x])) { options->include_pkgs = g_slist_prepend(options->include_pkgs, (gpointer) g_strdup(pkgs[x])); } x++; } g_strfreev(pkgs); g_free(content); } } } // Process update_md_paths if (options->update_md_paths && !options->update) g_warning("Usage of --update-md-path without --update has no effect!"); x = 0; while (options->update_md_paths && options->update_md_paths[x] != NULL) { char *path = options->update_md_paths[x]; options->l_update_md_paths = g_slist_prepend(options->l_update_md_paths, (gpointer) path); x++; } if (options->discard_additional_metadata) { options->keep_all_metadata = FALSE; } // Check discard-additional-metadata if (options->discard_additional_metadata && !options->update) { g_warning("--discard-additional-metadata has no effect (--update is not used)"); } // Process --distro tags x = 0; while (options->distro_tags && options->distro_tags[x]) { if (!strchr(options->distro_tags[x], ',')) { options->distro_cpeids = g_slist_append(options->distro_cpeids, NULL); options->distro_values = g_slist_append(options->distro_values, g_strdup(options->distro_tags[x])); x++; continue; } gchar **items = g_strsplit(options->distro_tags[x++], ",", 2); if (!items) continue; if (!items[0] || !items[1] || items[1][0] == '\0') { g_strfreev(items); continue; } if (items[0][0] != '\0') options->distro_cpeids = g_slist_append(options->distro_cpeids, g_strdup(items[0])); else options->distro_cpeids = g_slist_append(options->distro_cpeids, NULL); options->distro_values = g_slist_append(options->distro_values, g_strdup(items[1])); g_strfreev(items); } // Check retain-old-md-by-age if (options->retain_old_md_by_age) { if (options->retain_old) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "--retain-old-md-by-age cannot be combined " "with --retain-old-md"); return FALSE; } // Parse argument if (!parse_period_of_time(options->retain_old_md_by_age, &options->md_max_age, err)) return FALSE; } // check if --revision is numeric, when --set-timestamp-to-revision is given if (options->set_timestamp_to_revision) { char *endptr; gint64 revision = strtoll(options->revision, &endptr, 0); if (endptr == options->revision || *endptr != '\0') { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "--set-timestamp-to-revision require numeric value for --revision"); return FALSE; } if ((errno == ERANGE && revision == LLONG_MAX) || revision < 0) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "--revision value out of range"); return FALSE; } } #ifdef CR_DELTA_RPM_SUPPORT // Check oldpackagedirs x = 0; while (options->oldpackagedirs && options->oldpackagedirs[x]) { char *path = options->oldpackagedirs[x]; options->oldpackagedirs_paths = g_slist_prepend( options->oldpackagedirs_paths, (gpointer) path); x++; } #endif // Check cut_dirs if (options->cut_dirs < 0) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "--cur-dirs value must be positive integer"); return FALSE; } // Zchunk options if (options->zck_dict_dir && !options->zck_compression) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Cannot use --zck-dict-dir without setting --zck"); return FALSE; } // Zchunk options if (options->general_compression_type == CR_CW_ZCK_COMPRESSION && options->zck_compression) { g_set_error(err, ERR_DOMAIN, CRE_BADARG, "Cannot use --general-compress-type=zck with --zck"); return FALSE; } if (options->zck_dict_dir) options->zck_dict_dir = cr_normalize_dir_path(options->zck_dict_dir); return TRUE; } void free_options(struct CmdOptions *options) { g_free(options->basedir); g_free(options->location_base); g_free(options->outputdir); g_free(options->pkglist); g_free(options->checksum); g_free(options->compress_type); g_free(options->groupfile); g_free(options->groupfile_fullpath); g_free(options->revision); g_free(options->retain_old_md_by_age); g_free(options->cachedir); g_free(options->checksum_cachedir); g_strfreev(options->excludes); g_strfreev(options->includepkg); g_strfreev(options->distro_tags); g_strfreev(options->content_tags); g_strfreev(options->repo_tags); #ifdef CR_DELTA_RPM_SUPPORT g_strfreev(options->oldpackagedirs); #endif cr_slist_free_full(options->include_pkgs, g_free); cr_slist_free_full(options->exclude_masks, (GDestroyNotify) g_pattern_spec_free); cr_slist_free_full(options->l_update_md_paths, g_free); cr_slist_free_full(options->distro_cpeids, g_free); cr_slist_free_full(options->distro_values, g_free); cr_slist_free_full(options->modulemd_metadata, g_free); g_slist_free(options->oldpackagedirs_paths); }