/* change-archive.c
*
* vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
****************************************************************
* Copyright (C) 2005 Canonical Limited
* Authors: Robert Collins <robert.collins@canonical.com>
*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1