/* archive.c:
*
* vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
****************************************************************
* Copyright (C) 2003 Tom Lord
*
* 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/bugs/panic.h"
#include "hackerlab/os/errno.h"
#include "hackerlab/os/errno-to-string.h"
#include "hackerlab/os/sys/types.h"
#include "hackerlab/os/sys/wait.h"
#include "hackerlab/os/signal.h"
#include "hackerlab/os/time.h"
#include "hackerlab/os/unistd.h"
#include "hackerlab/os/stdlib.h"
#include "hackerlab/fmt/cvt.h"
#include "hackerlab/mem/alloc-limits.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/mem/talloc.h"
#include "hackerlab/hash/hash-utils.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/char/str.h"
#include "hackerlab/char/char-class.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/fs/file-names.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/rmrf.h"
#include "libfsutils/copy-file.h"
#include "libawk/trim.h"
#include "libarch/namespace.h"
#include "libarch/my.h"
#include "libarch/exec.h"
#include "libarch/archives.h"
#include "libarch/archive-version.h"
#include "libarch/debug.h"
#include "libarch/pfs.h"
#include "libarch/archive-pfs.h"
#include "libarch/cached-archive.h"
#include "libarch/arch-cache.h"
#include "libarch/archive.h"
/* __STDC__ prototypes for static functions */
static int invoke_tar_extract (int * pid_ret);
static int wait_for_tar (int pid);
static void ensure_writable (struct arch_archive * arch, int mirror_ok);
static struct arch_archive * connect_if_url (t_uchar const * branch, t_uchar ** branch_out);
static struct arch_archive * connect_if_not_url (t_uchar const * branch, t_uchar ** branch_out, struct arch_archive * (*connect_function)(t_uchar const *));
static struct arch_archive * arch_archive_connected_one_of (ar_archive_location const locations);
static struct arch_archive * arch_archive_connect_first_location (t_uchar const * official_name, ar_archive_location const locations);
static int writable_archive (struct arch_archive *archive);
static struct arch_archive * connected_by_url = 0;
/**
* \brief register a connected archive in the archive registry
*/
void
arch_archive_register (struct arch_archive * archive)
{
int present = arch_archives_archive_has_location_ext (archive->official_name, archive->location, 1);
if (!present)
{
arch_archive_location_t * location;
inifile_t inifile;
t_uchar * value;
arch_archives_get_archive_ini_no_default (archive->official_name, &inifile);
location = arch_archive_location_new (archive->location);
if (archive->mirror_of)
location->master = 0;
else
location->master = 2;
if (str_cmp (archive->registered_name, archive->official_name))
location->priority = 50;
if (writable_archive (archive))
location->readonly = 0;
else
location->readonly = 2;
value = arch_archive_location_to_ini_key(location);
inifile_add_key (&inifile, "", "url", value, "");
arch_archives_save_archive_ini_no_default (archive->official_name, &inifile);
inifile_finalise (&inifile);
talloc_free (location);
lim_free (0, value);
archive->in_registry = -1;
}
}
void
arch_make_archive (t_uchar * name, t_uchar * location, t_uchar * mirror_of, int dot_listing_lossage, int signed_archive, int tla_archive, int register_archive)
{
t_uchar * current_loc = 0;
t_uchar * version;
invariant (arch_valid_archive_name (name));
current_loc = arch_archive_location (name, 1);
if (current_loc && !mirror_of)
{
safe_printfmt (2, "arch_make_archive: archive already registered\n name: %s\n", name);
exit (2);
}
version = arch_archive_version_for_new_archive (tla_archive);
if (arch_cached_archive_protocol (location))
arch_cached_make_archive (name, location, version, mirror_of, dot_listing_lossage, signed_archive);
else
{
if (str_cmp_prefix ("uncached:", location) == 0)
location = location + str_length ("uncached:");
arch_pfs_make_archive (name, location, version, mirror_of, dot_listing_lossage, signed_archive);
}
if (register_archive)
{
struct arch_archive *arch = arch_archive_connect_location (location, 0);
arch_archive_register (arch);
arch_archive_close (arch);
}
arch_run_hook ("make-archive", "ARCH_ARCHIVE", name, "ARCH_LOCATION", location, 0);
lim_free (0, version);
}
t_uchar *
arch_archive_not_writable (struct arch_archive *arch, int mirror_ok)
{
if (arch->access != arch_archive_writable)
{
return str_alloc_cat_many (0, "attempt to modify archive that has ",
"read-only compatibility: ",
arch->official_name, " url: ",
arch->location,
"\n",
str_end);
}
if (!mirror_ok)
{
if (!arch->client_anticipates_mirror && !!arch->mirror_of)
{
return str_alloc_cat_many (0, "attempt to commit directly to mirror\n",
" archive: ", arch->location, "\n",
"mirror of: ", arch->official_name, "\n",
str_end);
}
if (arch->client_anticipates_mirror && !arch->mirror_of)
{
return str_alloc_cat_many(0, "archive is not a mirror of ",
arch->client_anticipates_mirror, "\n",
" archive: ", arch->official_name, "\nurl: ",
arch->location, "\n",
" expected mirror of",
arch->client_anticipates_mirror, "\n",
"\n",
str_end);
}
if (arch->client_anticipates_mirror && str_cmp (arch->client_anticipates_mirror, arch->mirror_of))
{
return str_alloc_cat_many(0, "attempt to write to wrong mirror\n",
" url: ", arch->location, "\n",
" expected mirror:",
arch->client_anticipates_mirror, "\n",
" got mirror of: ", arch->mirror_of, "\n",
str_end);
}
}
return NULL;
}
/**
* \brief connect to a url, with an expected name
*/
struct arch_archive *
arch_archive_connect_location (t_uchar const * location, int soft_errors)
{
struct arch_archive *result;
for (result = connected_by_url;
result && str_cmp (result->location, location);
result = result->next)
;
if (result)
{
invariant (!!talloc_reference (NULL, result));
}
else
{
result = arch_archive_connect_location_ext (NULL, location, NULL, soft_errors, soft_errors);
if (!result)
{
if (!soft_errors)
panic ("failed to connect to location with soft_errors zero");
return NULL;
}
}
return result;
}
/**
* \brief get the signed status from an inifile, with a overridable default
*/
arch_fail_action_t
arch_archive_inifile_signed (struct arch_archive * arch, inifile_t *inifile, arch_fail_action_t default_value)
{
arch_fail_action_t result;
t_uchar * temp_string = inifile_get_single_string (inifile, "", "when_unsigned", "default");
if (!str_casecmp ("ignore", temp_string))
result = arch_fail_ignore;
else if (!str_casecmp ("warn", temp_string))
result = arch_fail_warn;
else if (!str_casecmp ("error", temp_string))
result = arch_fail_error;
else if (!str_casecmp ("default", temp_string))
result = default_value;
else
{
result = default_value;
safe_printfmt (2, "invalid value '%s' for when_unsigned for archive %s\n", temp_string, arch->official_name);
}
lim_free (0, temp_string);
return result;
}
/**
* \brief set the signed satatus for an inifile
*/
void
arch_archive_inifile_set_signed (inifile_t *inifile, arch_fail_action_t value)
{
t_uchar * temp_string;
switch (value)
{
default:panic ("unknown fail_action value");
case arch_fail_ignore:
temp_string = "ignore";break;
case arch_fail_warn:
temp_string = "warn";break;
case arch_fail_error:
temp_string = "error";break;
}
inifile_set_single_string (inifile, "", "when_unsigned", temp_string, "");
}
/**
* \brief connect to a url, with the ability to override the signature checking policy
*
* returned handles should be closed with arch_archive_close
*/
struct arch_archive *
arch_archive_connect_location_ext (t_uchar const * name, t_uchar const * location, t_uchar const * want_mirror_of, int soft_errors, int override_signed)
{
struct arch_archive * answer;
int err;
int cached_protocol;
/* FIXME RBC 20050322 only the concrete classes should alloc.
* Right way: pass name and client_anticipates to all the connect concrete calls
*/
answer = talloc (NULL, struct arch_archive);
mem_set0 ((t_uchar *)answer, sizeof (*answer));
talloc_set_destructor (answer, arch_archive_finalise);
answer->client_anticipates_mirror = (want_mirror_of ? str_save (0, want_mirror_of) : 0);
answer->registered_name = str_save (0, name);
cached_protocol = arch_cached_archive_protocol (location);
if (cached_protocol)
arch_ensure_cache_path ();
if (cached_protocol && arch_cache_active ())
{
answer->location = str_save (0, location);
err = arch_cached_archive_connect (&answer, soft_errors);
}
else
{
t_uchar * uncached_location = arch_uncached_location(location);
answer->location = str_save (0, uncached_location + str_length ("uncached:"));
lim_free (0, uncached_location);
err = arch_pfs_archive_connect (&answer, soft_errors);
}
if (err)
{
if (soft_errors)
{
if (talloc_free (answer))
debug (dbg_archive, 0, _("Failed during free of archive\n"));
return NULL;
}
panic ("failed to connect to location.");
}
answer->version = answer->vtable->archive_version (answer);
if (!answer->version)
{
if (soft_errors)
{
if (talloc_free (answer))
debug (dbg_archive, 0, _("Failed during free of archive\n"));
return NULL;
}
safe_printfmt (2, "arch_archive_connect_location_ext: unidentifiable archive (%s)\n", answer->location);
exit (2);
}
answer->access = arch_archive_access (answer->version);
answer->type = arch_archive_type (answer->version);
if (answer->access == arch_archive_incompatible)
{
safe_printfmt (2, "arch_archive_connect: attempt to connect to incompatible archive\n archive: %s\n", (name ? name : location));
if (soft_errors)
{
if (talloc_free (answer))
debug (dbg_archive, 0, _("Failed during free of archive\n"));
return NULL;
}
else
exit (2);
}
{
t_uchar *temp = arch_get_meta_info (answer, "mirror");
answer->mirror_of = str_save (0, trim_surrounding_ws (temp));
lim_free (0, temp);
temp = arch_get_meta_info (answer, "name");
answer->official_name = str_save (0, trim_surrounding_ws (temp));
lim_free (0, temp);
}
if (!answer->official_name)
{
int ign;
printfmt (&ign, 2, "Unable to read the official name of archive %s\n Archive damaged?\n", name);
if (soft_errors)
{
if (talloc_free (answer))
debug (dbg_archive, 0, _("Failed during free of archive\n"));
return NULL;
}
else
exit (2);
}
lim_free (0, answer->registered_name);
answer->registered_name = str_save (0, answer->official_name);
answer->http_blows = arch_get_meta_int_info (answer, "http-blows");
answer->signed_archive = arch_get_meta_int_info (answer, "signed-archive");
if (arch_archive_has_registry_entry (answer))
{
inifile_t inifile;
arch_archives_get_archive_ini (answer->official_name, &inifile);
answer->when_unsigned = arch_archive_inifile_signed (answer, &inifile, arch_fail_ignore);
inifile_finalise (&inifile);
}
if (want_mirror_of && str_cmp (want_mirror_of, answer->mirror_of))
{
safe_printfmt (2, "arch_archive_connect: attempt to connect to wrong mirror\n archive: %s\n wanted mirror of: %s\n got mirror of: %s\n",
(name ? name : location), want_mirror_of, (answer->mirror_of ? answer->mirror_of : (t_uchar *)"(not a mirror)"));
if (soft_errors)
{
if (talloc_free (answer))
debug (dbg_archive, 0, _("Failed during free of archive\n"));
return NULL;
}
else
exit (2);
}
if (!override_signed && arch_archive_check_signed_status (answer, 2))
{
if (soft_errors)
{
if (talloc_free (answer))
debug (dbg_archive, 0, _("Failed during free of archive\n"));
return NULL;
}
else
exit (2);
}
if (answer)
{
answer->next = connected_by_url;
connected_by_url = answer;
}
return answer;
}
struct arch_archive *
arch_archive_connect_ext (t_uchar const * const name, t_uchar const * const want_mirror_of, int soft_errors)
{
struct arch_archive * answer = 0;
/* FIXME-REMOVENAME to change this, we need to be given connection
* requests by oficial name only, with an alternative api for
* locations, or more options - i.e. give me a mirror, give me a
* writable location.
* 1) find matching official name
* 2) if it doesn't fit the requested options, keep searching
*/
t_uchar * location = 0;
location = arch_archive_location (name, 0);
answer = arch_archive_connect_location_ext (name, location, want_mirror_of, soft_errors, 0);
if (!answer)
{
if (soft_errors)
{
lim_free (0, location);
return NULL;
}
panic ("failed to connect to location with soft_errors zero");
}
lim_free (0, answer->registered_name);
answer->registered_name = str_save (0, name);
lim_free(0, location);
return answer;
}
/**
* \brief connect to a url archive. If the url looks like an archive name
* or branch name, don't connect.
* \param branch the branch to conenct to
* \param branch_out where to store the official namespace if we connect
*/
struct arch_archive *
connect_if_url (t_uchar const * branch, t_uchar ** branch_out)
{
struct arch_archive * result = NULL;
if (!arch_valid_package_name (branch, arch_maybe_archive, arch_req_package, 1)
&& !arch_valid_archive_name (branch))
{
/* maybe url */
/* try as archive */
t_uchar * temp_location = escape_location (branch);
result = arch_archive_connect_location (temp_location, 1);
if (result)
{
arch_archive_register (result);
if (branch_out)
*branch_out = str_save (0, result->official_name);
}
else
{
t_uchar *rel_part = str_chr_rindex (branch, '/') + 1;
if ((rel_part - 1) && str_length (rel_part) && rel_part - 1 > branch)
{
t_uchar *location = str_save_n (0, branch, rel_part - 1 - branch);
location = str_replace (location, escape_location (location));
result = arch_archive_connect_location (location, 1);
if (result)
{
arch_archive_register (result);
if (branch_out)
*branch_out = str_alloc_cat_many (0, result->official_name, "/", rel_part, str_end);
}
lim_free (0, location);
}
}
lim_free (0, temp_location);
}
return result;
}
/**
* \brief connect to a non url branch, with a supplied policy connection routine.
*/
struct arch_archive *
connect_if_not_url (t_uchar const * branch, t_uchar ** branch_out, struct arch_archive * (*connect_function)(t_uchar const *))
{
struct arch_archive * result = NULL;
if (arch_valid_archive_name (branch))
{
result = connect_function (branch);
if (result && branch_out)
*branch_out = str_save (0, result->official_name);
}
else if (arch_valid_package_name (branch, arch_maybe_archive, arch_req_category, 1))
{
t_uchar * archive_name = arch_parse_package_name (arch_ret_archive, NULL, branch);
result = connect_function (archive_name);
lim_free (0, archive_name);
if (result && branch_out)
{
t_uchar *nonarch = arch_parse_package_name (arch_ret_non_archive, NULL, branch);
*branch_out = str_alloc_cat_many (0, result->official_name, "/", nonarch, str_end);
lim_free (0, nonarch);
}
}
return result;
}
/**
* \brief connect to a readonly or better branch (or revision).
*
* \param branch the branch to connect to - url, or namespace
* \param branch_out if not NULL this is set to a namespace value
* \return struct_arch_archive * a connected archive on success, or NULL on failure.
*/
struct arch_archive *
arch_archive_connect_branch (t_uchar const * branch, t_uchar ** branch_out)
{
struct arch_archive * result = connect_if_url (branch, branch_out);
if (!result)
result = connect_if_not_url (branch, branch_out, arch_archive_connect_readonly);
return result;
}
/**
* \brief connect to a writable or better branch (or revision).
*
* if a url is supplied, it is presumed to be writable or better.
* \param branch the branch to connect to - url, or namespace
* \param branch_out if not NULL this is set to a namespace value
* \return struct_arch_archive * a connected archive on success, or NULL on failure.
*/
struct arch_archive *
arch_archive_connect_writeable_branch (t_uchar const * branch, t_uchar ** branch_out)
{
struct arch_archive * result = connect_if_url (branch, branch_out);
if (!result)
result = connect_if_not_url (branch, branch_out, arch_archive_connect_writable);
return result;
}
/**
* \brief connect to a commitable or better branch (or revision).
*
* if a url is supplied, it is presumed to be commitable or better.
* \param branch the branch to connect to - url, or namespace
* \param branch_out if not NULL this is set to a namespace value
* \return struct_arch_archive * a connected archive on success, or NULL on failure.
*/
struct arch_archive *
arch_archive_connect_commitable_branch (t_uchar const * branch, t_uchar ** branch_out)
{
struct arch_archive * result = connect_if_url (branch, branch_out);
if (!result)
result = connect_if_not_url (branch, branch_out, arch_archive_connect_commitable);
return result;
}
/**
* \brief find any one of a set of locations in the connected set of archives
* \param locations the locations to query
* \return NULL if none is found.
*/
struct arch_archive *
arch_archive_connected_one_of (ar_archive_location const locations)
{
struct arch_archive *result;
for (result = connected_by_url;
result; result = result->next)
{
int index;
ar_for_each (locations, index)
{
if (!str_cmp (result->location, locations[index]->url))
return result;
}
}
return result;
}
/**
* \brief connect to an archive by name when commitable access is needed
* \param name the official name of the archive
* \return struct arch_archive *
*/
struct arch_archive *
arch_archive_connect_commitable (t_uchar const * official_name)
{
struct arch_archive * answer = 0;
ar_archive_location locations = arch_archive_locations (official_name);
{
int index;
ar_for_each (locations, index)
{
if (locations[index]->readonly || !locations[index]->master)
{
talloc_free (locations[index]);
ar_remove_archive_location(&locations, index);
--index;
}
}
}
if (!ar_size_archive_location (locations))
{
debug (dbg_archive, 1, "No commitable locations for %s are registered\n", official_name);
return NULL;
}
answer = arch_archive_connected_one_of (locations);
if (answer)
invariant (!!talloc_reference (NULL, answer));
else
answer = arch_archive_connect_first_location (official_name, locations);
ar_free_archive_location (&locations);
return answer;
}
/**
* \brief connect to an archive by name when write access is needed
* \param name the official name of the archive
* \return struct arch_archive *
*/
struct arch_archive *
arch_archive_connect_writable (t_uchar const * official_name)
{
struct arch_archive * answer = 0;
ar_archive_location locations = arch_archive_locations (official_name);
{
int index;
ar_for_each (locations, index)
{
if (locations[index]->readonly)
{
talloc_free (locations[index]);
ar_remove_archive_location(&locations, index);
--index;
}
}
}
if (!ar_size_archive_location (locations))
{
debug (dbg_archive, 1, "No writable locations for %s are registered\n", official_name);
return NULL;
}
answer = arch_archive_connected_one_of (locations);
if (answer)
invariant (!!talloc_reference (NULL, answer));
else
answer = arch_archive_connect_first_location (official_name, locations);
ar_free_archive_location (&locations);
return answer;
}
/**
* \brief connect to the first accessible archive in an ordered list
* \param locations the locations to try in order
*/
struct arch_archive *
arch_archive_connect_first_location (t_uchar const * official_name, ar_archive_location const locations)
{
struct arch_archive * result = NULL;
int index;
ar_for_each (locations, index)
{
result = arch_archive_connect_location_ext (official_name, locations[index]->url, NULL, 1, 0);
if (result)
return result;
}
return result;
}
/**
* \brief connect to an archive by name when only read access is needed
* \param name the official name of the archive
* \return struct arch_archive *
*/
struct arch_archive *
arch_archive_connect_readonly (t_uchar const * official_name)
{
struct arch_archive * answer = 0;
ar_archive_location locations = arch_archive_locations (official_name);
if (!ar_size_archive_location (locations))
return NULL;
answer = arch_archive_connected_one_of (locations);
if (answer)
invariant (!!talloc_reference (NULL, answer));
else
answer = arch_archive_connect_first_location (official_name, locations);
ar_free_archive_location (&locations);
return answer;
}
int
arch_archive_finalise (void *archive_void)
{
struct arch_archive * arch = (struct arch_archive *) archive_void;
{
struct arch_archive ** handle = &connected_by_url;
while (*handle)
{
if (*handle != arch)
handle = &((*handle)->next);
else
{
*handle = (*handle)->next;
}
}
}
lim_free (0, arch->location);
lim_free (0, arch->mirror_of);
lim_free (0, arch->registered_name);
lim_free (0, arch->official_name);
lim_free (0, arch->version);
return 0;
}
t_uchar *
arch_archive_version (struct arch_archive * arch)
{
return arch->vtable->archive_version (arch);
}
rel_table
arch_archive_categories (struct arch_archive * arch)
{
rel_table answer = 0;
answer = arch->vtable->categories (arch);
rel_sort_table_by_field (0, answer, 0);
return answer;
}
rel_table
arch_archive_branches (struct arch_archive * arch, t_uchar * category)
{
rel_table answer = 0;
answer = arch->vtable->branches (arch, category);
rel_sort_table_by_field (0, answer, 0);
return answer;
}
rel_table
arch_archive_versions (struct arch_archive * arch, t_uchar * package)
{
rel_table answer = 0;
answer = arch->vtable->versions (arch, package);
arch_sort_table_by_name_field (0, answer, 0);
return answer;
}
t_uchar *
arch_archive_latest_revision (struct arch_archive * arch, t_uchar * unknown_component, int full)
{
rel_table revs = 0;
t_uchar * answer = 0;
t_uchar * version = 0;
/* First, see if we were just given a version */
if ( arch_valid_package_name ( unknown_component, arch_no_archive, arch_req_version, 0))
{
version = str_save(0, unknown_component);
}
/* Ok. Not a version. Lets see if its a package */
else if (arch_valid_package_name (unknown_component, arch_no_archive, arch_req_package, 0))
{
rel_table versions = 0;
versions = arch_archive_versions (arch, unknown_component);
arch_sort_table_by_name_field (1, versions, 0);
if (! versions)
{
safe_printfmt (2, "arch_archive_latest_revision: ");
safe_printfmt (2, "package %s has no versions\n", unknown_component);
exit(1);
}
version = str_save(0, versions[0][0]);
}
/* Uh oh. we were given nothing we can use */
else
{
safe_printfmt(2, "arch_archive_versions: %s neither package nor version.\n",
unknown_component);
exit(1);
}
revs = arch_archive_revisions (arch, version, full);
if (revs)
answer = str_save (0, revs[rel_n_records (revs) - 1][0]);
rel_free_table (revs);
lim_free (0, version);
return answer;
}
rel_table
arch_archive_revisions (struct arch_archive * arch, t_uchar const * version, int full)
{
rel_table answer = 0;
answer = arch->vtable->revisions (arch, (t_uchar *)version);
arch_sort_table_by_patch_level_field (0, answer, 0);
if (full)
{
t_uchar * fqv = 0;
int x;
if (full == 1)
fqv = arch_fully_qualify (arch->official_name, (t_uchar *)version);
else
fqv = str_save (0, version);
for (x = 0; x < rel_n_records (answer); ++x)
{
t_uchar * t = str_alloc_cat_many (0, fqv, "--", answer[x][0], str_end);
rel_replace_record (answer, x, rel_make_record (t, NULL));
lim_free (0, t);
}
lim_free (0, fqv);
}
return answer;
}
t_uchar *
arch_archive_log (struct arch_archive * arch, t_uchar * revision)
{
t_uchar * answer = 0;
answer = arch->vtable->archive_log (arch, revision);
return answer;
}
/* RBC 20050109 the api here needs an overhaul.
* is_cached should rather be a bitfield or just return into
* a struct
*/
void
arch_revision_type (enum arch_revision_type * type, int * is_cached, int *has_ancestry,
struct arch_archive * arch, arch_patch_id * revision)
{
/* TRANSITIONAL : remove when pools are implemented */
invariant_str_cmp (arch->official_name, arch_patch_id_archive (revision));
if (arch->vtable->revision_type (type, is_cached, has_ancestry, arch, arch_patch_id_revision (revision)) < 0)
{
safe_printfmt (2, "failed to query archive:\n revision: %s\n location: %s\n", arch_patch_id_patch_id (revision), arch->location);
exit (2);
}
}
enum arch_revision_type
arch_archive_get_revision_type (struct arch_archive * arch, t_uchar const * revision)
{
enum arch_revision_type type;
arch_patch_id * revision_patch = arch_patch_id_new_archive (arch->official_name, revision);
arch_revision_type (&type, NULL, NULL, arch, revision_patch);
talloc_free (revision_patch);
return type;
}
int
arch_revision_exists (struct arch_archive * arch, t_uchar * revision)
{
enum arch_revision_type type;
return (arch->vtable->revision_type (&type, NULL, NULL, arch, revision) >= 0);
}
void
arch_get_patch_targz (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
arch->vtable->get_patch (out_fd, arch, revision);
}
void
arch_get_patch (struct arch_archive * arch, arch_patch_id * const revision, t_uchar const * const dest_dir)
{
int here_fd;
t_uchar * dest_dir_dir = 0;
t_uchar * dest_dir_dir_path = 0;
t_uchar * dest_dir_tail = 0;
t_uchar * dest_dir_path = 0;
t_uchar * tmpdir_path = 0;
int out_fd = 0;
int tar_pid = 0;
t_uchar * patches_file_name = 0;
t_uchar * patches_file_dest = 0;
/* TRANSITIONAL : remove when pools are implemented */
invariant_str_cmp (arch->official_name, arch_patch_id_archive (revision));
here_fd = safe_open (".", O_RDONLY, 0);
dest_dir_dir = file_name_directory_file (0, dest_dir);
if (dest_dir_dir)
safe_chdir (dest_dir_dir);
dest_dir_dir_path = safe_current_working_directory ();
dest_dir_tail = file_name_tail (0, dest_dir);
dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);
tmpdir_path = talloc_tmp_file_name (talloc_context, dest_dir_dir_path, ",,get-patch");
patches_file_name = str_alloc_cat (0, arch_patch_id_revision (revision), ".patches");
patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);
safe_mkdir (tmpdir_path, 0777);
safe_chdir (tmpdir_path);
out_fd = invoke_tar_extract (&tar_pid);
arch->vtable->get_patch (out_fd, arch, arch_patch_id_revision (revision));
safe_close (out_fd);
if (wait_for_tar (tar_pid))
panic ("arch_get_patch: tar exitted with non-0 status");
safe_rename (patches_file_name, patches_file_dest);
safe_fchdir (here_fd);
safe_close (here_fd);
rmrf_file (tmpdir_path);
lim_free (0, dest_dir_dir);
lim_free (0, dest_dir_dir_path);
lim_free (0, dest_dir_tail);
lim_free (0, dest_dir_path);
talloc_free (tmpdir_path);
lim_free (0, patches_file_name);
lim_free (0, patches_file_dest);
}
void
arch_get_cached_revision_targz (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
arch->vtable->get_cached (out_fd, arch, revision);
}
void
arch_get_cached_revision (struct arch_archive * arch, arch_patch_id * const revision, t_uchar const * const dest_dir)
{
int here_fd;
t_uchar * dest_dir_dir = 0;
t_uchar * dest_dir_dir_path = 0;
t_uchar * dest_dir_tail = 0;
t_uchar * dest_dir_path = 0;
t_uchar * tmpdir_path = 0;
int out_fd = 0;
int tar_pid = 0;
t_uchar * patches_file_dest = 0;
/* TRANSITIONAL : remove when pools are implemented */
invariant_str_cmp (arch->official_name, arch_patch_id_archive (revision));
here_fd = safe_open (".", O_RDONLY, 0);
dest_dir_dir = file_name_directory_file (0, dest_dir);
if (dest_dir_dir)
safe_chdir (dest_dir_dir);
dest_dir_dir_path = safe_current_working_directory ();
dest_dir_tail = file_name_tail (0, dest_dir);
dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);
tmpdir_path = talloc_tmp_file_name (talloc_context, dest_dir_dir_path, ",,get-patch");
patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);
safe_mkdir (tmpdir_path, 0777);
safe_chdir (tmpdir_path);
out_fd = invoke_tar_extract (&tar_pid);
arch->vtable->get_cached (out_fd, arch, arch_patch_id_revision (revision));
safe_close (out_fd);
if (wait_for_tar (tar_pid))
panic ("arch_get_patch: tar exitted with non-0 status");
safe_rename (arch_patch_id_revision (revision), patches_file_dest);
safe_fchdir (here_fd);
safe_close (here_fd);
rmrf_file (tmpdir_path);
lim_free (0, dest_dir_dir);
lim_free (0, dest_dir_dir_path);
lim_free (0, dest_dir_tail);
lim_free (0, dest_dir_path);
talloc_free (tmpdir_path);
lim_free (0, patches_file_dest);
}
void
arch_get_import_targz (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
arch->vtable->get_import (out_fd, arch, revision);
}
void
arch_get_import_revision (struct arch_archive * arch, arch_patch_id * const revision, t_uchar const * const dest_dir)
{
int here_fd;
t_uchar * dest_dir_dir = 0;
t_uchar * dest_dir_dir_path = 0;
t_uchar * dest_dir_tail = 0;
t_uchar * dest_dir_path = 0;
t_uchar * tmpdir_path = 0;
int out_fd = 0;
int tar_pid = 0;
t_uchar * patches_file_dest = 0;
/* TRANSITIONAL : remove when pools are implemented */
invariant_str_cmp (arch->official_name, arch_patch_id_archive (revision));
here_fd = safe_open (".", O_RDONLY, 0);
dest_dir_dir = file_name_directory_file (0, dest_dir);
if (dest_dir_dir)
safe_chdir (dest_dir_dir);
dest_dir_dir_path = safe_current_working_directory ();
dest_dir_tail = file_name_tail (0, dest_dir);
dest_dir_path = file_name_in_vicinity (0, dest_dir_dir_path, dest_dir_tail);
tmpdir_path = talloc_tmp_file_name (talloc_context, dest_dir_dir_path, ",,get-patch");
patches_file_dest = file_name_in_vicinity (0, "..", dest_dir_tail);
safe_mkdir (tmpdir_path, 0777);
safe_chdir (tmpdir_path);
out_fd = invoke_tar_extract (&tar_pid);
arch->vtable->get_import (out_fd, arch, arch_patch_id_revision (revision));
safe_close (out_fd);
if (wait_for_tar (tar_pid))
panic ("arch_get_patch: tar exitted with non-0 status");
safe_rename (arch_patch_id_revision (revision), patches_file_dest);
safe_fchdir (here_fd);
safe_close (here_fd);
rmrf_file (tmpdir_path);
lim_free (0, dest_dir_dir);
lim_free (0, dest_dir_dir_path);
lim_free (0, dest_dir_tail);
lim_free (0, dest_dir_path);
talloc_free (tmpdir_path);
lim_free (0, patches_file_dest);
}
arch_patch_id *
arch_get_continuation (struct arch_archive * arch, arch_patch_id * revision)
{
t_uchar * raw_data = 0;
t_uchar * start;
t_uchar * end;
arch_patch_id * answer = NULL;
/* TRANSITIONAL : remove when pools are implemented */
invariant_str_cmp (arch->official_name, arch_patch_id_archive (revision));
raw_data = arch->vtable->get_continuation (arch, arch_patch_id_revision (revision));
start = raw_data;
while (*start && char_is_space (*start))
++start;
end = start;
while (*end && !char_is_space (*end))
++end;
{
t_uchar * temp = str_save_n (0, start, end - start);
answer = arch_patch_id_new (temp);
lim_free (0, temp);
}
lim_free (0, raw_data);
return answer;
}
/**
* \brief Set a meta-info value in an archive
* \param archive The archive to modify
* \param meta_info_name The name to modify
* \param meta_info_value The value to assign. If NULL, the value is unset.
* \return 0 on success, other values on failure
* \note This operation is non-transactional.
*/
int arch_set_meta_info (struct arch_archive * archive,
t_uchar * meta_info_name,
t_uchar * meta_info_value)
{
return archive->vtable->set_meta_info (archive, meta_info_name, meta_info_value);
}
t_uchar *
arch_get_meta_info (struct arch_archive * arch, t_uchar * meta_info_name)
{
t_uchar * answer = 0;
answer = arch->vtable->get_meta_info (arch, meta_info_name);
return answer;
}
int
arch_make_category (t_uchar ** errstr,
struct arch_archive * arch, t_uchar * category)
{
int answer;
ensure_writable (arch, 0);
answer = arch->vtable->make_category (errstr, arch, category);
arch_run_hook ("make-category", "ARCH_ARCHIVE", arch->official_name, "ARCH_CATEGORY", category, 0);
return answer;
}
int
arch_make_branch (t_uchar ** errstr,
struct arch_archive * arch, t_uchar * branch)
{
int answer;
ensure_writable (arch, 0);
answer = arch->vtable->make_branch (errstr, arch, branch);
arch_run_hook ("make-branch", "ARCH_ARCHIVE", arch->official_name, "ARCH_BRANCH", branch, 0);
return answer;
}
int
arch_make_version (t_uchar ** errstr,
struct arch_archive * arch, t_uchar * version)
{
int answer;
ensure_writable (arch, 0);
answer = arch->vtable->make_version (errstr, arch, version);
arch_run_hook ("make-version", "ARCH_ARCHIVE", arch->official_name, "ARCH_VERSION", version, 0);
return answer;
}
int
arch_archive_lock_revision (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * new_level)
{
t_uchar *local_id = 0;
int result;
ensure_writable (a, 1);
if (!txn_id)
{
local_id = arch_generate_txn_id ();
txn_id = local_id;
}
result = a->vtable->lock_revision (errstr, a, version, prev_level, uid, txn_id, new_level);
lim_free (0, local_id);
return result;
}
int
arch_revision_ready (t_uchar ** errstr, struct arch_archive *a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * new_level)
{
return a->vtable->revision_ready (errstr, a, NULL, version, prev_level, uid, txn_id, new_level);
}
int
arch_mirror_revision_ready (t_uchar ** errstr, struct arch_archive *a,
struct arch_archive *from_archive,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * new_level)
{
return a->vtable->revision_ready (errstr, a, from_archive, version, prev_level, uid, txn_id, new_level);
}
int
arch_archive_finish_revision (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * new_level)
{
ensure_writable (a, 0);
return a->vtable->finish_revision (errstr, a, version, prev_level, uid, txn_id, new_level);
}
enum arch_revision_lock_state
arch_archive_revision_lock_state (t_uchar ** prev_level_ret,
t_uchar ** uid_ret,
t_uchar ** txn_id_ret,
struct arch_archive * a,
t_uchar * version)
{
ensure_writable (a, 1);
return a->vtable->lock_state (prev_level_ret, uid_ret, txn_id_ret, a, version);
}
int
arch_archive_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id)
{
ensure_writable (a, 1);
return a->vtable->break_revision_lock (errstr, a, version, prev_level, uid, txn_id);
}
int
arch_archive_put_log (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * log_text)
{
ensure_writable (a, 0);
return a->vtable->put_log (errstr, a, version, prev_level, uid, txn_id, log_text);
}
int
arch_archive_put_continuation (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * continuation)
{
ensure_writable (a, 0);
return a->vtable->put_continuation (errstr, a, version, prev_level, uid, txn_id, continuation);
}
int
arch_archive_put_changeset_targz (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * level,
int in_fd)
{
int answer;
answer = a->vtable->put_changeset (errstr, a, version, prev_level, uid, txn_id, level, in_fd);
return answer;
}
int
arch_archive_put_changeset (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * level,
t_uchar * dir)
{
t_uchar * dir_tail = 0;
t_uchar * desired_name = 0;
t_uchar * tar_file_path = 0;
int in_fd;
int answer;
dir_tail = file_name_tail (0, dir);
desired_name = str_alloc_cat_many (0, version, "--", level, ".patches", str_end);
invariant (!str_cmp (dir_tail, desired_name));
/* GNU tar 1.13 is busted and doesn't output to pipes correctly.
*/
ensure_writable (a, 0);
tar_file_path = make_tmp_tar_archive (dir);
in_fd = safe_open (tar_file_path, O_RDONLY, 0);
answer = a->vtable->put_changeset (errstr, a, version, prev_level, uid, txn_id, level, in_fd);
safe_close (in_fd);
safe_unlink (tar_file_path);
lim_free (0, dir_tail);
lim_free (0, desired_name);
lim_free (0, tar_file_path);
return answer;
}
int
arch_archive_put_import_targz (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * level,
int in_fd)
{
int answer;
answer = a->vtable->put_import (errstr, a, version, prev_level, uid, txn_id, level, in_fd);
return answer;
}
int
arch_archive_put_import (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id,
t_uchar * level,
t_uchar * dir)
{
t_uchar * dir_tail = 0;
t_uchar * desired_name = 0;
t_uchar * tar_file_path = 0;
int in_fd;
int answer;
dir_tail = file_name_tail (0, dir);
desired_name = str_alloc_cat_many (0, version, "--", level, str_end);
invariant (!str_cmp (dir_tail, desired_name));
/* GNU tar 1.13 is busted and doesn't output to pipes correctly.
*/
ensure_writable (a, 0);
tar_file_path = make_tmp_tar_archive (dir);
in_fd = safe_open (tar_file_path, O_RDONLY, 0);
answer = a->vtable->put_import (errstr, a, version, prev_level, uid, txn_id, level, in_fd);
safe_close (in_fd);
safe_unlink (tar_file_path);
lim_free (0, dir_tail);
lim_free (0, desired_name);
lim_free (0, tar_file_path);
return answer;
}
int
arch_archive_put_cached_targz (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd)
{
return a->vtable->put_cached (errstr, a, NULL, revision, in_fd);
}
int
arch_archive_mirror_cached_targz (t_uchar ** errstr, struct arch_archive * a, struct arch_archive *from_archive, t_uchar * revision, int in_fd)
{
return a->vtable->put_cached (errstr, a, from_archive, revision, in_fd);
}
int
arch_archive_put_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, t_uchar * dir)
{
t_uchar * dir_tail = 0;
t_uchar * tar_file_path = 0;
int in_fd;
int answer;
dir_tail = file_name_tail (0, dir);
invariant (!str_cmp (dir_tail, revision));
/* GNU tar 1.13 is busted and doesn't output to pipes correctly.
*/
ensure_writable (a, 1);
tar_file_path = make_tmp_tar_archive (dir);
in_fd = safe_open (tar_file_path, O_RDONLY, 0);
answer = arch_archive_put_cached_targz (errstr, a, revision, in_fd);
safe_close (in_fd);
safe_unlink (tar_file_path);
lim_free (0, dir_tail);
lim_free (0, tar_file_path);
return answer;
}
int
arch_archive_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
ensure_writable (a, 1);
return a->vtable->delete_cached (errstr, a, revision);
}
void
arch_archive_repair_non_txnal (int chatter_fd, struct arch_archive * a)
{
ensure_writable (a, 1);
a->vtable->repair_non_txnal (chatter_fd, a);
}
void
arch_archive_get_ancestry (int out_fd, struct arch_archive * arch, t_uchar * revision)
{
arch->vtable->get_ancestry (out_fd, arch, revision);
}
int
arch_archive_put_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision, int in_fd)
{
return a->vtable->put_ancestry (errstr, a, NULL, revision, in_fd);
}
int
arch_archive_mirror_ancestry (t_uchar ** errstr, struct arch_archive * a, struct arch_archive *from_archive, t_uchar * revision, int in_fd)
{
return a->vtable->put_ancestry (errstr, a, from_archive, revision, in_fd);
}
int
arch_archive_delete_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
ensure_writable (a, 1);
return a->vtable->delete_ancestry (errstr, a, revision);
}
t_uchar *
arch_generate_txn_id (void)
{
time_t now;
pid_t pid;
char hostname[64];
char number[64];
t_uchar * answer;
now = time (0);
pid = getpid ();
mem_set0 ((t_uchar *)hostname, sizeof (hostname));
gethostname (hostname, sizeof (hostname));
cvt_ulong_to_hex (number, hash_ul((t_ulong)now ^ (t_ulong)pid));
answer = str_save (0, number);
cvt_ulong_to_hex (number, hash_mem ((t_uchar *)hostname, sizeof (hostname)));
answer = str_realloc_cat (0, answer, number);
cvt_ulong_to_hex (number, hash_ul ((t_ulong)now ^ ~(t_ulong)pid));
answer = str_realloc_cat (0, answer, number);
return answer;
}
arch_patch_id *
arch_previous_revision (struct arch_archive * arch, arch_patch_id * revision)
{
enum arch_patch_level_type current_type;
enum arch_patch_level_type next_type;
t_ulong current_n;
t_ulong next_n;
t_uchar * next_patch_level = 0;
arch_patch_id * answer = 0;
/* TRANSITIONAL : remove when pools are implemented */
invariant_str_cmp (arch->official_name, arch_patch_id_archive (revision));
current_type = arch_analyze_patch_level (¤t_n, arch_patch_id_patchlevel (revision));
if (current_type == arch_is_base0_level)
{
answer = NULL;
}
else
{
if (((current_type == arch_is_patch_level) || (current_type == arch_is_versionfix_level)) && (current_n > 1))
{
next_type = current_type;
next_n = current_n - 1;
}
else if (current_n == 1)
{
next_n = 0;
if (current_type == arch_is_patch_level)
next_type = arch_is_base0_level;
else
next_type = arch_is_version_level;
}
else
{
rel_table revisions = 0;
int x;
revisions = arch_archive_revisions (arch, arch_patch_id_version (revision), 0);
if (!revisions)
{
safe_printfmt (2, "version has no revisions (%s) in url: %s\n", arch_patch_id_patch_id (revision), arch->location);
exit (2);
}
for (x = 0; x < rel_n_records (revisions); ++x)
if (revisions[x][0][0] == 'v')
break;
invariant (x);
if (x == rel_n_records (revisions))
x = rel_n_records (revisions);
if (x == 1)
{
next_type = arch_is_base0_level;
next_n = 0;
}
else
{
next_type = arch_is_patch_level;
next_n = x - 1;
}
rel_free_table (revisions);
}
next_patch_level = arch_form_patch_level (next_type, next_n);
{
t_uchar * temp_answer = str_alloc_cat_many (0, arch_patch_id_version (revision), "--", next_patch_level, str_end);
answer = arch_patch_id_new_archive (arch->official_name, temp_answer);
lim_free (0, temp_answer);
}
}
lim_free (0, next_patch_level);
return answer;
}
t_uchar *
arch_ancestor_revision (struct arch_archive * arch, t_uchar * revision)
{
enum arch_revision_type type;
arch_patch_id * revision_patch = arch_patch_id_new_archive (arch->official_name, revision);
arch_revision_type (&type, NULL, NULL, arch, revision_patch);
switch (type)
{
default:
{
panic ("arch_ancestor_revision: unrecognized revision type");
talloc_free (revision_patch);
return 0;
}
case arch_import_revision:
{
talloc_free (revision_patch);
return 0;
}
case arch_simple_revision:
{
arch_patch_id * prev_revision = 0;
t_uchar * answer = 0;
prev_revision = arch_previous_revision (arch, revision_patch);
answer = str_save (0, arch_patch_id_patch_id (prev_revision));
talloc_free (prev_revision);
talloc_free (revision_patch);
return answer;
}
case arch_continuation_revision:
{
arch_patch_id * result = arch_get_continuation (arch, revision_patch);
t_uchar * answer = str_save (0, arch_patch_id_patch_id (result));
talloc_free (revision_patch);
talloc_free (result);
return answer;
}
}
}
/* this creates a temporary file name for use in archive
* operations.
*/
t_uchar *
archive_tmp_file_name (t_uchar * dir, t_uchar * basename)
{
t_uchar * my_uid = 0;
t_uchar * tmp_name = 0;
t_uchar * talloced_tmp;
my_uid = arch_my_id_uid_default ("Unknown User <example@example.com>");
talloced_tmp = talloc_tmp_file_name (talloc_context, dir, basename);
tmp_name = str_alloc_cat_many (0, talloced_tmp, ".", my_uid, str_end);
lim_free (0, my_uid);
talloc_free (talloced_tmp);
return tmp_name;
}
static int
invoke_tar_extract (int * pid_ret)
{
int pipe_fds[2];
int pid;
if (pipe (pipe_fds))
panic ("unable to create pipe fds for tar");
pid = fork ();
if (pid == -1)
panic ("unable to fork for patch");
if (pid)
{
*pid_ret = pid;
safe_close (pipe_fds[0]);
return pipe_fds[1];
}
else
{
t_uchar ** argv;
safe_close (pipe_fds[1]);
argv = 0;
ar_push_uchar_star (&argv, cfg__gnu_tar);
ar_push_uchar_star (&argv, "-m");
ar_push_uchar_star (&argv, "--preserve");
ar_push_uchar_star (&argv, "-zxf");
ar_push_uchar_star (&argv, "-");
ar_push_uchar_star (&argv, 0);
safe_move_fd (pipe_fds[0], 0);
arch_util_execvp (cfg__gnu_tar, argv);
panic ("invoke_tar_extract: execvp for patch returned to caller");
exit (2);
}
panic ("invoke_tar_extract: not reached");
return -1;
}
static int
wait_for_tar (int pid)
{
int status;
int wait_pid;
wait_pid = waitpid (pid, &status, 0);
if (wait_pid < 0)
{
panic_msg ("error waiting for tar subprocess");
kill (0, SIGKILL);
panic ("error waiting for subprocess");
}
if (WIFSIGNALED (status))
{
safe_printfmt (2, "\n");
safe_printfmt (2, "wait_for_tar: tar subprocess killed by signal %d\n", WTERMSIG (status));
safe_printfmt (2, "\n");
exit (2);
return -1;
}
else if (!WIFEXITED (status))
{
panic_msg ("waitpid returned for a non-exited process");
kill (0, SIGKILL);
panic ("waitpid returned for a non-exited process");
return -1;
}
else
{
int exit_status;
exit_status = WEXITSTATUS (status);
return exit_status;
}
}
t_uchar *
make_tmp_tar_archive (t_uchar * dir)
{
t_uchar * dir_dir = 0;
t_uchar * dir_tail = 0;
t_uchar * tmp_stem = 0;
t_uchar * tmp_in_cwd = 0;
t_uchar * tmp_path = 0;
dir_dir = file_name_directory_file (0, dir);
if (!dir_dir)
dir_dir = str_save (0, ".");
dir_tail = file_name_tail (0, dir);
tmp_stem = str_alloc_cat_many (0, ",,", dir_tail, ".tar.gz", str_end);
tmp_in_cwd = talloc_tmp_file_name (talloc_context, ".", tmp_stem);
tmp_path = file_name_in_vicinity (0, dir_dir, tmp_in_cwd);
{
int pid;
int dev_null_fd;
dev_null_fd = safe_open ("/dev/null", O_WRONLY, 0);
pid = fork ();
if (pid == -1)
panic ("unable to fork for patch");
if (!pid)
{
t_uchar ** argv;
safe_chdir (dir_dir);
argv = 0;
ar_push_uchar_star (&argv, cfg__gnu_tar);
ar_push_uchar_star (&argv, "--force-local");
ar_push_uchar_star (&argv, "-zcf");
ar_push_uchar_star (&argv, tmp_in_cwd);
ar_push_uchar_star (&argv, dir_tail);
ar_push_uchar_star (&argv, 0);
safe_move_fd (dev_null_fd, 1);
safe_dup2 (1, 2);
arch_util_execvp (cfg__gnu_tar, argv);
panic ("make_tmp_tar_archive: execvp for patch returned to caller");
exit (2);
}
else
{
int status;
int wait_pid;
safe_close (dev_null_fd);
wait_pid = waitpid (pid, &status, 0);
if (wait_pid < 0)
{
panic_msg ("error waiting for tar subprocess");
kill (0, SIGKILL);
panic ("error waiting for subprocess");
}
if (WIFSIGNALED (status))
{
safe_printfmt (2, "\n");
safe_printfmt (2, "wait_for_tar: tar subprocess killed by signal %d\n", WTERMSIG (status));
safe_printfmt (2, "\n");
exit (2);
return 0;
}
else if (!WIFEXITED (status))
{
panic_msg ("waitpid returned for a non-exited process");
kill (0, SIGKILL);
panic ("waitpid returned for a non-exited process");
return 0;
}
else
{
int exit_status;
exit_status = WEXITSTATUS (status);
if (exit_status)
panic ("make_tmp_tar_archive: tar exitted with non-0 status");
}
}
}
lim_free (0, dir_dir);
lim_free (0, dir_tail);
talloc_free (tmp_in_cwd);
lim_free (0, tmp_stem);
return tmp_path;
}
static void
ensure_writable (struct arch_archive * arch, int mirror_ok)
{
t_uchar * write_failure;
write_failure = arch_archive_not_writable (arch, mirror_ok);
if (write_failure)
{
safe_printfmt (2, write_failure);
exit(2);
}
}
int
arch_get_meta_int_info(struct arch_archive * arch, t_uchar * key)
{
t_uchar * key_existence = arch_get_meta_info (arch, key);
int result = !!key_existence;
lim_free (0, key_existence);
return result;
}
/**
* Performs a tar on the specified dir, writing output to out_fd
* out_fd The file descriptor to write to
* dir The directory to tar up
*/
static void tar_to_fd (int out_fd, t_uchar * dir)
{
t_uchar * tar_file_path = make_tmp_tar_archive (dir);
int in_fd = safe_open (tar_file_path, O_RDONLY, 0);
copy_fd (in_fd, out_fd);
safe_unlink (tar_file_path);
safe_close (in_fd);
}
/**
* If the archive is cached, cache this changeset
* arch The archive containing the revision
* revision The revision of the changeset
* changeset_path The path to the changeset directory
*/
extern void arch_maybe_cache_commit (struct arch_archive *arch,
t_uchar * revision,
t_uchar * anc_archive,
t_uchar * anc_revision,
t_uchar * changeset_path)
{
if (str_cmp(arch->vtable->type, "cache") != 0)
return;
else
{
t_uchar * query = arch_revision_query (arch, revision, "delta.tar.gz");
t_uchar * tmp_name = 0;
int put_fd = arch_cache_put (&tmp_name, query);
t_uchar * fq_ancestor = arch_fully_qualify (anc_archive, anc_revision);
tar_to_fd (put_fd, changeset_path);
safe_close (put_fd);
arch_cache_commit (tmp_name, query);
lim_free (0, query);
lim_free (0, tmp_name);
query = arch_revision_query (arch, revision, "ancestor");
arch_cache_put_line (query, fq_ancestor);
lim_free (0, query);
lim_free (0, fq_ancestor);
}
}
t_uchar *
arch_fs_archive_category_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * category)
{
if (arch->type == arch_archive_baz)
panic ("baz does not support stand alone categories");
return file_name_in_vicinity (0, archive_path, category);
}
t_uchar *
arch_fs_archive_branch_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * package)
{
t_uchar * category = 0;
t_uchar * category_path = 0;
t_uchar * branch_path = 0;
if (arch->type == arch_archive_baz)
panic ("baz does not support stand alone branches");
invariant (arch_valid_package_name (package, arch_no_archive, arch_req_package, 0));
category = arch_parse_package_name (arch_ret_category, 0, package);
category_path = arch_fs_archive_category_path (arch, archive_path, category);
branch_path = file_name_in_vicinity (0, category_path, package);
lim_free (0, category);
lim_free (0, category_path);
return branch_path;
}
t_uchar *
arch_fs_archive_version_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar * version)
{
t_uchar * package = 0;
t_uchar * package_path = 0;
t_uchar * version_path = 0;
invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));
if (arch->type == arch_archive_baz)
return str_save (0, version);
package = arch_parse_package_name (arch_ret_package, 0, version);
package_path = arch_fs_archive_branch_path (arch, archive_path, package);
version_path = file_name_in_vicinity (0, package_path, version);
lim_free (0, package);
lim_free (0, package_path);
return version_path;
}
t_uchar *
arch_fs_archive_revision_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
t_uchar * version = 0;
t_uchar * version_path = 0;
t_uchar * level = 0;
t_uchar * revision_path = 0;
invariant (arch_valid_package_name (revision, arch_no_archive, arch_req_patch_level, 0));
version = arch_parse_package_name (arch_ret_package_version, 0, revision);
version_path = arch_fs_archive_version_path (arch, archive_path, version);
level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
revision_path = file_name_in_vicinity (0, version_path, level);
lim_free (0, version);
lim_free (0, version_path);
lim_free (0, level);
return revision_path;
}
t_uchar *
arch_fs_archive_revision_log_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
t_uchar * revision_path = 0;
t_uchar * log_path = 0;
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
log_path = file_name_in_vicinity (0, revision_path, "log");
lim_free (0, revision_path);
return log_path;
}
t_uchar *
arch_fs_archive_changeset_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
t_uchar * revision_path = 0;
t_uchar * changeset_path;
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
changeset_path = file_name_in_vicinity (0, revision_path, revision);
changeset_path = str_realloc_cat (0, changeset_path, ".patches.tar.gz");
lim_free (0, revision_path);
return changeset_path;
}
t_uchar *
arch_fs_archive_import_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
t_uchar * revision_path = 0;
t_uchar * changeset_path;
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
changeset_path = file_name_in_vicinity (0, revision_path, revision);
changeset_path = str_realloc_cat (0, changeset_path, ".src.tar.gz");
lim_free (0, revision_path);
return changeset_path;
}
t_uchar *
arch_fs_archive_cached_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
t_uchar * revision_path = 0;
t_uchar * changeset_path;
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
changeset_path = file_name_in_vicinity (0, revision_path, revision);
changeset_path = str_realloc_cat (0, changeset_path, ".tar.gz");
lim_free (0, revision_path);
return changeset_path;
}
static t_uchar *
arch_fs_archive_per_revision_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision, t_uchar const *filename)
{
t_uchar * revision_path = 0;
t_uchar * checksum_path = 0;
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
checksum_path = file_name_in_vicinity (0, revision_path, filename);
lim_free (0, revision_path);
return checksum_path;
}
t_uchar *
arch_fs_archive_cached_checksum_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
return arch_fs_archive_per_revision_path (arch, archive_path, revision, "checksum.cacherev");
}
t_uchar *
arch_fs_archive_ancestry_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
return arch_fs_archive_per_revision_path (arch, archive_path, revision, "ancestry.gz");
}
t_uchar *
arch_fs_archive_ancestry_checksum_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
return arch_fs_archive_per_revision_path (arch, archive_path, revision, "ancestry.gz.checksum");
}
t_uchar *
arch_fs_archive_continuation_path (struct arch_archive * arch, t_uchar * archive_path, t_uchar const * revision)
{
t_uchar * revision_path = 0;
t_uchar * continuation_path;
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
continuation_path = file_name_in_vicinity (0, revision_path, "CONTINUATION");
lim_free (0, revision_path);
return continuation_path;
}
t_uchar *
arch_fs_archive_revision_lock_unlocked_path (struct arch_archive * arch,
t_uchar * archive_path,
t_uchar * version,
t_uchar * prev_level)
{
t_uchar * answer = 0;
invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));
if (!prev_level)
{
t_uchar * version_path = 0;
version_path = arch_fs_archive_version_path (arch, archive_path, version);
answer = file_name_in_vicinity (0, version_path, "++revision-lock");
lim_free (0, version_path);
}
else
{
t_uchar * revision = 0;
t_uchar * revision_path = 0;
revision = str_alloc_cat_many (0, version, "--", prev_level, str_end);
revision_path = arch_fs_archive_revision_path (arch, archive_path, revision);
answer = file_name_in_vicinity (0, revision_path, "++revision-lock");
lim_free (0, revision);
lim_free (0, revision_path);
}
return answer;
}
t_uchar *
arch_fs_archive_revision_lock_locked_path (struct arch_archive * arch,
t_uchar * archive_path,
t_uchar * version,
t_uchar * prev_level,
t_uchar * arch_user_id,
t_uchar * txn_id)
{
t_uchar * version_path = 0;
t_uchar * lock_basename = 0;
t_uchar * answer = 0;
invariant (arch_valid_package_name (version, arch_no_archive, arch_req_version, 0));
version_path = arch_fs_archive_version_path (arch, archive_path, version);
lock_basename = str_alloc_cat_many (0,
"++revision-lock-held--",
(prev_level ? prev_level : (t_uchar *)"absolute-0"),
"--",
arch_user_id,
(txn_id ? "--" : 0),
txn_id,
str_end);
answer = file_name_in_vicinity (0, version_path, lock_basename);
lim_free (0, version_path);
lim_free (0, lock_basename);
return answer;
}
t_uchar *
arch_fs_archive_revision_lock_locked_contents_path (struct arch_archive * arch,
t_uchar * archive_path,
t_uchar * version,
t_uchar * prev_level,
t_uchar * arch_user_id,
t_uchar * txn_id)
{
t_uchar * locked_path = 0;
t_uchar * answer = 0;
locked_path = arch_fs_archive_revision_lock_locked_path (arch, archive_path, version, prev_level, arch_user_id, txn_id);
answer = file_name_in_vicinity (0, locked_path, "+contents");
lim_free (0, locked_path);
return answer;
}
t_uchar *
arch_fs_archive_revision_lock_broken_path (struct arch_archive * arch,
t_uchar * archive_path,
t_uchar * version,
t_uchar * prev_level)
{
t_uchar * version_path = 0;
t_uchar * lock_dir_basename = 0;
t_uchar * lock_basename = 0;
t_uchar * broken_dir = 0;
t_uchar * answer = 0;
version_path = arch_fs_archive_version_path (arch, archive_path, version);
lock_dir_basename = str_alloc_cat (0, "++revision-lock-broken--", (prev_level ? prev_level : (t_uchar *)"absolute-0"));
lock_basename = str_alloc_cat (0, ",,remade-lock--", (prev_level ? prev_level : (t_uchar *)"absolute-0"));
broken_dir = file_name_in_vicinity (0, version_path, lock_dir_basename);
answer = file_name_in_vicinity (0, broken_dir, lock_basename);
lim_free (0, version_path);
lim_free (0, lock_dir_basename);
lim_free (0, lock_basename);
lim_free (0, broken_dir);
return answer;
}
void
arch_archive_connection_cache_init (arch_archive_connection_cache * cache)
{
cache->cache = NULL;
}
void
arch_archive_connection_cache_finalise (arch_archive_connection_cache * cache, struct arch_archive * dontclose)
{
int position;
for (position = 0; position < ar_size_arch_archive (cache->cache); ++position)
{
if (cache->cache[position] != dontclose)
arch_archive_close (cache->cache[position]);
}
ar_free_arch_archive (&cache->cache);
cache->cache = NULL;
}
struct arch_archive *
arch_archive_connection_cache_find_or_maybe_connect (arch_archive_connection_cache * cache, t_uchar *name, int soft_errors)
{
int position;
ar_archive_location locations;
struct arch_archive * arch = 0;
for (position = 0; position < ar_size_arch_archive (cache->cache); ++position)
{
if (!str_cmp (name, cache->cache[position]->official_name))
return (cache->cache)[position];
}
locations = arch_archive_locations (name);
if (ar_size_archive_location(locations))
{
arch = arch_archive_connect_branch (name, NULL);
invariant (!arch || !str_cmp (name, arch->official_name));
if (arch)
arch_archive_connection_cache_add (cache, arch);
}
ar_free_archive_location (&locations);
return arch;
}
void
arch_archive_connection_cache_add (arch_archive_connection_cache * cache, struct arch_archive * arch)
{
ar_push_arch_archive (&cache->cache, arch);
}
/**
* \brief determine if arch has any registry-style configuration details
* \param arch the archive to query
* \return non zero if the arch does have registry style config details
*/
int
arch_archive_has_registry_entry (struct arch_archive * arch)
{
arch->in_registry = arch_archives_has_registry_entry (arch->official_name);
return arch->in_registry;
}
/**
* \brief check an archives signed status against its expected status
* \param fd the fd to output warnings on
* \param archive the archive
* \return 0 if the archive handle can be used to read data
*/
int
arch_archive_check_signed_status (struct arch_archive *archive, int status_fd)
{
if (archive->signed_archive)
return 0;
if (!archive->when_unsigned)
return 0;
if (status_fd > -1 )
{
safe_printfmt (status_fd, "\n");
safe_printfmt (status_fd, archive->when_unsigned == arch_fail_error ? _("ERROR: ") : _("WARNING: "));
safe_printfmt (status_fd, _("archive %s is configured as a signed archive, but its copy at location %s is not signed.\n"
"Someone may have tampered with this archive.\n"
"\n"),
archive->official_name, archive->location);
}
return archive->when_unsigned == arch_fail_error;
}
/**
* \brief inform an archive that it should be or not be a mirror
*/
int
arch_archive_set_mirror (t_uchar ** errstr, struct arch_archive *archive, int enabled)
{
int errn;
if ((enabled && archive->mirror_of) || (!enabled && !archive->mirror_of))
return 0;
if (!(errn = archive->vtable->set_mirror (errstr, archive, enabled)))
{
if (enabled)
archive->mirror_of = str_save (0, archive->official_name);
else
{
lim_free (0, archive->mirror_of);
archive->mirror_of = NULL;
}
}
return errn;
}
/**
* \brief strcmp that understands locations are urls
*/
int
arch_archive_cmp_location (t_uchar const *left, t_uchar const *right)
{
t_uchar *normleft = arch_uncached_location (left);
t_uchar *normright = arch_uncached_location (right);
int result = str_cmp (normleft, normright);
lim_free (0, normleft);
lim_free (0, normright);
return result;
}
/**
* \brief is this archive conenction cached ?
*
* while its bogus to have to ask this, until the archive format doesn't depend on bit equivalence for tarballs, we have to
*/
int
arch_archive_is_cached_connection (struct arch_archive *archive)
{
return !str_cmp (archive->vtable->type, "cache");
}
/**
* \brief Determine whether the archive is writable
* \param archive The archive to test
* \return true if the archive is writable, false if it's readonly
* \note This operation is race-safe, because it only depends on whether
* the file can be written, not its contents.
*/
static int
writable_archive (struct arch_archive *archive)
{
int writable = archive->access == arch_archive_writable;
if (!writable)
return writable;
writable = arch_set_meta_info (archive, "writable", "test") == 0;
if (writable)
arch_set_meta_info (archive, "writable", NULL);
return writable;
}
/* tag: Tom Lord Tue May 20 00:52:06 2003 (archive.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1