/* change-archive.c * * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2 **************************************************************** * Copyright (C) 2005 Canonical Limited * Authors: Robert Collins * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "config-options.h" #include "po/gettext.h" #include "hackerlab/bugs/exception.h" #include "hackerlab/cmd/main.h" #include "hackerlab/fs/file-names.h" #include "libarch/my.h" #include "libarch/archive.h" #include "libarch/archives.h" #include "libarch/archive-mirror.h" #include "libarch/cached-archive.h" #include "libarch/pfs.h" #include "libfsutils/tmp-files.h" #include "commands/version.h" #include "commands/change-archive.h" static t_uchar * usage = N_("[options] archivename|URL"); #define OPTS(OP) \ OP (opt_help_msg, "h", "help", 0, \ N_("Display a help message and exit.")) \ OP (opt_long_help, "H", 0, 0, \ N_("Display a verbose help message and exit.")) \ OP (opt_version, "V", "version", 0, \ N_("Display a release identifier string\n" \ "and exit.")) \ OP (opt_add_signatures, 0, "add-signatures", 0, \ N_("turn this archve into a signed archive")) \ OP (opt_remove_signatures, 0, "remove-signatures", 0, \ N_("remove digital signatures from this archive")) t_uchar arch_change_archive_help[] = N_("Alter properties of an archive. For instance, add signatures to an " "unsigned archive.\n" "\n" "The following operations involve making a temporary mirror of the archive:\n" "- adding signatures\n" "- removing signatures\n" "- changing the archive format\n" "They may take some time as they involve a full mirror of the archive and all\n" "its contents\n" "The following operations will automatically propogate their changes to all\n" "writeable mirrors listed in ~/.arch-params/archives/NAME, and must have the\n" "master archive available to operate at all:\n" "- adding signatures\n" "- removing signatures\n"); enum options { OPTS (OPT_ENUM) }; static struct opt_desc opts[] = { OPTS (OPT_DESC) {-1, 0, 0, 0, 0} }; typedef struct change_options_ { /* should be url ?*/ t_uchar * archivename; int add_signatures; int remove_signatures; struct arch_archive *from; struct arch_archive *to; } change_options_t; static void simple_mirror (struct arch_archive *from, struct arch_archive *to) { struct arch_archive_mirror_options mirror_opts; mem_set0 ((t_uchar *)&mirror_opts, sizeof (mirror_opts)); /* TODO factor out a init method */ mirror_opts.print_summary = 0; mirror_opts.cachedrevs = arch_mirror_all_cachedrevs; arch_archive_mirror (-1, from, to, NULL, &mirror_opts); } static int rename_helper (struct arch_pfs_session *pfs, t_uchar **errstr, t_uchar * from, t_uchar *to) { t_uchar *from_relpath = file_name_tail (0, from); t_uchar *to_relpath = file_name_tail (0, to); int result = arch_pfs_rename (pfs, errstr, from_relpath, to_relpath, 1); if (result) safe_printfmt (2, "failed to rename from %s to %s\n", from_relpath, to_relpath); lim_free (0, from_relpath); lim_free (0, to_relpath); return result; } static int change_archive (change_options_t *changes) { int must_mirror = 0; int result = 0; t_uchar * archive_parent; t_uchar * archive_dirpath; t_uchar * temp_archivepath = NULL; t_uchar * temp_oldarchivepath; t_uchar * original_location; t_uchar * tempname = NULL; struct arch_pfs_session * pfs = NULL; t_uchar * errstr = NULL; int target_signed; int debug = 1; inifile_t inifile; if (changes->add_signatures || changes->remove_signatures) must_mirror = 1; if (!must_mirror) { /* no ops yet */ return 0; } /* connect */ changes->from = arch_archive_connect_commitable_branch (changes->archivename, NULL); if (!changes->from) goto error_exit; /* determine target options */ if (changes->add_signatures) target_signed = 1; else if (changes->remove_signatures) target_signed = 0; else target_signed = changes->from->signed_archive; if (changes->add_signatures || changes->remove_signatures) { /* must have a master */ if (changes->from->mirror_of) { safe_printfmt (2, "Cannot add or remove signatures from mirrors, only from the master archive\n"); goto error_exit; } } /* get location */ archive_parent = file_name_directory_file (0, changes->from->location); archive_parent = str_replace (archive_parent, arch_uncached_location (archive_parent)); archive_parent = str_replace (archive_parent, str_save (0, archive_parent + 9)); archive_dirpath = file_name_directory_file (0, changes->from->location); archive_dirpath = str_replace (archive_dirpath, arch_uncached_location (archive_dirpath)); /* make archive at location's basepath */ if (debug) safe_printfmt (2, "Creating temporary archive\n"); temp_archivepath = talloc_tmp_file_name (talloc_context, archive_dirpath, "change-archive"); tempname = str_alloc_cat (0, changes->from->official_name, "CHANGE"); arch_make_archive (tempname, temp_archivepath, changes->from->official_name, changes->from->http_blows, target_signed, arch_archive_tla == changes->from->type, 0); changes->to = arch_archive_connect_location_ext (tempname, temp_archivepath, changes->from->official_name, 1, 1); if (!changes->to) goto error_exit; /* FIXME get rid of the name bogon */ if (!changes->from->in_registry) changes->to->registered_name = str_replace (changes->to->registered_name, str_save (0, tempname)); /* add the mirror flag on the old one to prevent commits */ if (debug) safe_printfmt (2, "Converting existing archive to readonly status.\n"); arch_archive_set_mirror (&errstr, changes->from, 1); /* mirror */ if (debug) safe_printfmt (2, "Mirroring archive to temporary archive.\n"); simple_mirror (changes->from, changes->to); /* close handles. */ temp_oldarchivepath = file_name_directory_file (0, changes->from->location); temp_oldarchivepath = str_replace (temp_oldarchivepath, arch_uncached_location (archive_dirpath)); temp_oldarchivepath = talloc_tmp_file_name (temp_archivepath, temp_oldarchivepath, "change-archive-original"); original_location = str_save (0, changes->from->location); original_location = str_replace (original_location, arch_uncached_location (original_location)); arch_archive_close (changes->from); changes->from = NULL; arch_archive_close (changes->to); changes->to = NULL; /* rename old */ if (debug) safe_printfmt (2, "Renaming original archive out of the way.\n"); pfs = arch_pfs_connect (archive_parent, 1); if (!pfs) goto error_exit; if (rename_helper (pfs, &errstr, original_location, temp_oldarchivepath)) goto error_exit; /* rename new */ if (debug) safe_printfmt (2, "Renaming new archive into position.\n"); if (rename_helper (pfs, &errstr, temp_archivepath, original_location)) goto error_exit; /* reopen both again */ if (debug) safe_printfmt (2, "Reconnecting to original archive.\n"); changes->from = arch_archive_connect_location (temp_oldarchivepath, 1); if (!changes->from) goto error_exit; if (debug) safe_printfmt (2, "Reconnecting to new archive.\n"); changes->to = arch_archive_connect_location_ext (tempname, original_location, changes->from->official_name, 1, 1); if (!changes->to) goto error_exit; /* mirror again as a just-in-case */ if (debug) safe_printfmt (2, "Mirroring new changesets from original archive to new archive.\n"); simple_mirror (changes->from, changes->to); /* remove the mirror flag on the new one */ if (debug) safe_printfmt (2, "Converting new archive into a master archive.\n"); arch_archive_set_mirror (&errstr, changes->to, 0); /* update the archive registry */ if (debug) safe_printfmt (2, "Update the archive registry with new archives status.\n"); arch_archives_get_archive_ini_no_default (changes->from->official_name, &inifile); inifile_add_key (&inifile, "", "when_unsigned", target_signed ? "error" : "ignore", ""); arch_archives_save_archive_ini_no_default (changes->from->official_name, &inifile); /* remove the old archive*/ if (debug) safe_printfmt (2, "Removing the old archive.\n"); { t_uchar * relpath = file_name_tail (0, temp_oldarchivepath); arch_pfs_rmrf_file (pfs, relpath); lim_free (0, relpath); } if (debug) safe_printfmt (2, "Operation complete.\n"); goto ok_exit; error_exit: result = -1; if (errstr) safe_printfmt (2, _("Error during archive change: '%s'\n"), errstr); else safe_printfmt (2, _("Error during archive change: 'unknown'\n")); ok_exit: lim_free (0, tempname); arch_archive_close (changes->from); changes->from = NULL; arch_archive_close (changes->to); changes->to = NULL; if (pfs) arch_pfs_disconnect (&pfs); talloc_free (temp_archivepath); return result; } int arch_change_archive (t_uchar * program_name, int argc, char * argv[]) { int o; struct opt_parsed * option; change_options_t changes = {NULL, 0, 0, NULL, NULL}; int result; safe_buffer_fd (1, 0, O_WRONLY, 0); option = 0; while (1) { o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, libarch_version_string, arch_change_archive_help, opt_help_msg, opt_long_help, opt_version); if (o == opt_none) break; switch (o) { default: safe_printfmt (2, "unhandled option `%s'\n", option->opt_string); panic ("internal error parsing arguments"); usage_error: opt_usage (2, argv[0], program_name, usage, 1); exit (1); /* bogus_arg: */ safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string); goto usage_error; case opt_add_signatures: { changes.add_signatures = 1; changes.remove_signatures = 0; break; } case opt_remove_signatures: { changes.add_signatures = 0; changes.remove_signatures = 1; break; } } } if (argc > 2) goto usage_error; changes.archivename = str_save (0, argv[1]); result = change_archive (&changes); lim_free (0, changes.archivename); return result; }