From c94548b9fb22355dd732b5f88539661fe6514b5f Mon Sep 17 00:00:00 2001 From: Tomas Mlcoch Date: Thu, 28 Nov 2013 17:54:40 +0100 Subject: [PATCH] Final move with better atomicity --- src/createrepo_c.c | 115 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 22 deletions(-) diff --git a/src/createrepo_c.c b/src/createrepo_c.c index 9f52838..1b963ef 100644 --- a/src/createrepo_c.c +++ b/src/createrepo_c.c @@ -1417,16 +1417,48 @@ main(int argc, char **argv) g_free(repomd_path); - // Move files from out_repo into tmp_out_repo - - g_debug("Moving data from %s", out_repo); + // Final move if (g_file_test(out_repo, G_FILE_TEST_EXISTS)) { + g_debug("Copying files from old repository to the new one"); + + // Parse old repomd.xml + GError *tmp_err = NULL; + gchar *old_repomd_path = g_build_filename(out_repo, "repomd.xml", NULL); + cr_Repomd *repomd = cr_repomd_new(); + cr_xml_parse_repomd(old_repomd_path, repomd, NULL, NULL, &tmp_err); + if (tmp_err) { + g_warning("Cannot parse repomd: %s", old_repomd_path); + g_clear_error(&tmp_err); + cr_repomd_free(repomd); + repomd = cr_repomd_new(); + } + g_free(old_repomd_path); - // Delete old metadata - g_debug("Removing old metadata from %s", out_repo); - cr_remove_metadata_classic(out_dir, cmd_options->retain_old, NULL); + // Prepare list of basenames of metadata files in repomd.xml + GSList *old_basenames = NULL; + for (GSList *elem = repomd->records; elem; elem = g_slist_next(elem)) { + cr_RepomdRecord *rec = elem->data; - // Move files from out_repo to tmp_out_repo + if (!rec->location_href) { + // Ignore bad records (records without location_href) + g_warning("Record without location href in old repo"); + continue; + } + + if (rec->location_base) { + // Ignore files with base location + g_debug("Old repomd record with base location is ignored: " + "%s - %s", rec->location_base, rec->location_href); + continue; + } + + old_basenames = g_slist_prepend(old_basenames, + g_path_get_basename(rec->location_href)); + + } + + // Iterate over the files in the old repository and copy all + // that aren't listed in repomd.xml GDir *dirp; dirp = g_dir_open (out_repo, 0, NULL); if (!dirp) { @@ -1436,47 +1468,86 @@ main(int argc, char **argv) const gchar *filename; while ((filename = g_dir_read_name(dirp))) { + + if (g_slist_find_custom(old_basenames, filename, (GCompareFunc) g_strcmp0)) { + // This file is listed in repomd.xml, do not copy it + g_debug("Skipped file %s", filename); + continue; + } + gchar *full_path = g_strconcat(out_repo, filename, NULL); gchar *new_full_path = g_strconcat(tmp_out_repo, filename, NULL); // Do not override new file with the old one if (g_file_test(new_full_path, G_FILE_TEST_EXISTS)) { - g_debug("Skip move of: %s -> %s (the destination file already exists)", + g_debug("Skipped copy: %s -> %s (file already exists)", full_path, new_full_path); - g_debug("Removing: %s", full_path); - g_remove(full_path); g_free(full_path); g_free(new_full_path); continue; } - if (g_rename(full_path, new_full_path) == -1) - g_critical("Cannot move file %s -> %s", full_path, new_full_path); - else - g_debug("Moved %s -> %s", full_path, new_full_path); + // COPY! + cr_cp(full_path, + new_full_path, + CR_CP_RECURSIVE|CR_CP_PRESERVE_ALL, + NULL, + &tmp_err); + + if (tmp_err) { + g_warning("Cannot copy %s -> %s: %s", + full_path, new_full_path, tmp_err->message); + g_clear_error(&tmp_err); + } g_free(full_path); g_free(new_full_path); } + // Cleanup + cr_repomd_free(repomd); g_dir_close(dirp); - - // Remove out_repo - if (g_rmdir(out_repo) == -1) { - g_critical("Cannot remove %s", out_repo); - } else { - g_debug("Old out repo %s removed", out_repo); - } } + // === This section should be maximally atomic === + + sigset_t new_mask, old_mask; + sigemptyset(&old_mask); + sigfillset(&new_mask); + sigdelset(&new_mask, SIGKILL); // These two signals cannot be + sigdelset(&new_mask, SIGSTOP); // blocked + + sigprocmask(SIG_BLOCK, &new_mask, &old_mask); + + // Rename out_repo to "repodata.old" + gchar *old_repodata_path = g_build_filename(out_dir, "repodata.old", NULL); + if (g_rename(out_repo, old_repodata_path) == -1) { + g_error("Cannot rename %s -> %s", out_repo, old_repodata_path); + exit(EXIT_FAILURE); + } else { + g_debug("Renamed %s -> %s", out_repo, old_repodata_path); + } // Rename tmp_out_repo to out_repo if (g_rename(tmp_out_repo, out_repo) == -1) { - g_critical("Cannot rename %s -> %s", tmp_out_repo, out_repo); + g_error("Cannot rename %s -> %s", tmp_out_repo, out_repo); + exit(EXIT_FAILURE); } else { g_debug("Renamed %s -> %s", tmp_out_repo, out_repo); } + sigprocmask(SIG_SETMASK, &old_mask, NULL); + + // === End of section that has to be maximally atomic === + + // Remove "metadata.old" dir + if (cr_rm(old_repodata_path, CR_RM_RECURSIVE, NULL, &tmp_err)) { + g_debug("Old repo %s removed", old_repodata_path); + } else { + g_warning("Cannot remove %s: %s", old_repodata_path, tmp_err->message); + g_clear_error(&tmp_err); + } + // Clean up -- 2.7.4