/* cached-archive.c:
*
****************************************************************
* Copyright (C) 2003 Tom Lord
* Copyright (C) 2004 Aaron Bentley
*
* See the file "COPYING" for further information about
* the copyright and warranty status of this work.
*/
#include "hackerlab/vu/vu.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/char/str.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/mem/talloc.h"
#include "libfsutils/copy-file.h"
#include "libarch/archives.h"
#include "libarch/cached-archive.h"
#include "libarch/arch-cache.h"
#include "libarch/pfs.h"
#include "libawk/trim.h"
static void
copy_fd_close(int in_fd, int out_fd);
static int cache_close (void * arch_archive_void);
static t_uchar * cache_archive_version (struct arch_archive * a);
static rel_table cache_categories (struct arch_archive * a);
static rel_table cache_branches (struct arch_archive * a, t_uchar * category);
static rel_table cache_versions (struct arch_archive * a, t_uchar * package);
static rel_table cache_revisions (struct arch_archive * a, t_uchar * version);
static t_uchar * cache_archive_log (struct arch_archive * a, t_uchar * revision);
static int cache_revision_type (enum arch_revision_type * type, int * archive_cached,
int *has_ancestry,
struct arch_archive * a, t_uchar const * revision);
static void cache_get_patch (int out_fd, struct arch_archive * a, t_uchar * revision);
static void cache_get_cached (int out_fd, struct arch_archive * a, t_uchar * revision);
static void cache_get_import (int out_fd, struct arch_archive * a, t_uchar * revision);
static t_uchar * cache_get_continuation (struct arch_archive * a, t_uchar * revision);
static int cache_set_meta_info (struct arch_archive * a, t_uchar * meta_info_name, t_uchar * meta_info_value);
static t_uchar * cache_get_meta_info (struct arch_archive * a, t_uchar * meta_info_name);
static int cache_make_category (t_uchar ** errstr, struct arch_archive * a, t_uchar * category);
static int cache_make_branch (t_uchar ** errstr, struct arch_archive * a, t_uchar * branch);
static int cache_make_version (t_uchar ** errstr, struct arch_archive * a, t_uchar * version);
static int cache_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);
static int cache_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);
static int cache_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);
static enum arch_revision_lock_state cache_lock_state (t_uchar ** prev_level_ret,
t_uchar ** uid_ret,
t_uchar ** txn_id_ret,
struct arch_archive * a,
t_uchar * version);
static int cache_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id);
static int cache_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);
static int cache_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);
static int cache_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,
int in_fd);
static int cache_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,
int in_fd);
static int cache_put_cached (t_uchar ** errstr, struct arch_archive * a, struct arch_archive * from_archive,
t_uchar * revision, int in_fd);
static int cache_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision);
static void cache_repair_non_txnal (int chatter_fd, struct arch_archive * a);
static void cache_get_ancestry (int out_fd, struct arch_archive * a, t_uchar * revision);
static int cache_put_ancestry (t_uchar ** errstr, struct arch_archive * a, struct arch_archive * from_archive,
t_uchar * revision, int in_fd);
static int cache_delete_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision);
static int cache_set_mirror (t_uchar ** errstr, struct arch_archive * archive, int enabled);
static struct arch_archive_vtable cache_vtable =
{
"cache",
cache_archive_version,
cache_categories,
cache_branches,
cache_versions,
cache_revisions,
cache_archive_log,
cache_revision_type,
cache_get_patch,
cache_get_cached,
cache_get_import,
cache_get_continuation,
cache_set_meta_info,
cache_get_meta_info,
cache_make_category,
cache_make_branch,
cache_make_version,
cache_lock_revision,
cache_revision_ready,
cache_finish_revision,
cache_break_revision_lock,
cache_lock_state,
cache_put_log,
cache_put_continuation,
cache_put_changeset,
cache_put_import,
cache_put_cached,
cache_delete_cached,
cache_repair_non_txnal,
cache_get_ancestry,
cache_put_ancestry,
cache_delete_ancestry,
cache_set_mirror
};
/**
* Constructs a query string for a revision
* Returns a newly-allocated string
*/
extern t_uchar *
arch_revision_query (struct arch_archive * a, t_uchar const * revision,
t_uchar const * extension)
{
arch_patch_id patch;
t_uchar * query = 0;
arch_patch_id_init_archive (&patch, a->official_name, revision);
query =arch_cache_query_new (&patch, extension);
arch_patch_id_finalise (&patch);
return query;
}
/**
* Copies all data from in_fd to out_fd, closes in_fd
*/
static void
copy_fd_close(int in_fd, int out_fd)
{
copy_fd (in_fd, out_fd);
safe_close (in_fd);
}
/**
* Determines whether the provided location is a cache URL.
*/
int
arch_cached_archive_protocol (t_uchar const * location)
{
if (str_cmp_prefix ("cached:", location) == 0)
return 1;
else if (str_cmp_prefix ("uncached:", location) == 0)
return 0;
else return (!arch_pfs_is_local (location));
}
/**
* \brief Ensures a location has a single "uncached:" prefix.
* \param location The location to convert
* \return a new string, containing the modified location
*/
extern t_uchar *
arch_uncached_location (t_uchar const *location)
{
t_uchar const * new_location = location;
while (1)
{
if (str_cmp_prefix("cached:", new_location) == 0)
{
new_location = new_location + str_length ("cached:");
continue;
}
else if (str_cmp_prefix("uncached:", new_location) == 0)
{
new_location = new_location + str_length ("uncached:");
continue;
}
else
break;
}
return str_alloc_cat (0, "uncached:", new_location);
}
/* make a cached archive adaoter and connect,
* if soft_errors, returns -1 on error
*/
int
arch_cached_archive_connect (struct arch_archive ** answer, int soft_errors)
{
struct arch_cached_archive * arch = 0;
t_uchar * wrap_location = 0;
invariant (arch_cached_archive_protocol ((*answer)->location));
if (! arch_cache_active ())
{
safe_printfmt (2, "No cache attempting to connect to cached archive\n archive: %s\nlocation: %s\n", (*answer)->official_name, (*answer)->location);
if (soft_errors)
return -1;
else
exit (2);
}
arch = talloc_realloc_size (0, *answer, sizeof (struct arch_cached_archive));
talloc_set_destructor (arch, cache_close);
*answer = (struct arch_archive *)arch;
arch->arch.vtable = &cache_vtable;
wrap_location = arch_uncached_location ((*answer)->location);
arch->wrapped_arch = arch_archive_connect_location_ext ((*answer)->official_name, wrap_location, 0, soft_errors, 0);
lim_free (0, wrap_location);
if (arch->wrapped_arch)
{
/* this code section is copied from archive.c's .._connect, which this
* API is meant to duplicate
*/
/* what this does is set the name field in the wrapped archive
* to the name that the wrapping archive is connecting as
* this may be different from the official name.
* FIXME: remove this in baz 1.4 when we remove support for the old
* registered-names concept
* RBC 200503012
*/
lim_free (0, arch->wrapped_arch->registered_name);
arch->wrapped_arch->registered_name = str_save (0, (*answer)->registered_name);
/* save the policy details */
arch->arch.when_unsigned = arch->wrapped_arch->when_unsigned;
arch->arch.in_registry = arch->wrapped_arch->in_registry;
/* not sure about these, from the same block..
answer->refs = 1;
answer->next = connected_by_name;
connected_by_name = answer;*/
return 0;
}
if (soft_errors)
return -1;
else
exit (2);
}
void
arch_cached_make_archive (t_uchar * name, t_uchar * uri, t_uchar * version, t_uchar *mirror_of, int dot_listing_lossage, int signed_archive)
{
t_uchar * wrap_location = arch_uncached_location (uri);
invariant (arch_cached_archive_protocol (uri));
arch_make_archive (name, wrap_location, mirror_of, dot_listing_lossage, signed_archive, arch_archive_type(version) == arch_archive_tla, 0);
lim_free (0, wrap_location);
}
int
cache_close (void * arch_archive_void)
{
struct arch_archive * a = (struct arch_archive *) arch_archive_void;
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
if (wrapped_arch)
arch_archive_close (wrapped_arch);
wrapped_arch = NULL;
return arch_archive_finalise (a);
}
static t_uchar *
cache_archive_version (struct arch_archive * a)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->archive_version (wrapped_arch);
}
static rel_table
cache_categories (struct arch_archive * a)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->categories (wrapped_arch);
}
static rel_table
cache_branches (struct arch_archive * a, t_uchar * category)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->branches (wrapped_arch, category);
}
static rel_table
cache_versions (struct arch_archive * a, t_uchar * package)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->versions (wrapped_arch, package);
}
static rel_table
cache_revisions (struct arch_archive * a, t_uchar * version)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->revisions (wrapped_arch, version);
}
static t_uchar *
cache_archive_log (struct arch_archive * a, t_uchar * revision)
{
t_uchar * query = arch_revision_query (a, revision, "log");
t_uchar * answer = 0;
if (!arch_cache_has_answer (query))
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
answer = wrapped_arch->vtable->archive_log (wrapped_arch, revision);
arch_cache_put_str (query, answer);
}
else
answer = arch_cache_get_str (query);
lim_free (0, query);
return answer;
}
static t_uchar *
arch_revision_type_string (enum arch_revision_type type)
{
switch (type)
{
case arch_import_revision:
return "import";
case arch_simple_revision:
return "simple";
case arch_continuation_revision:
return "continuation";
default:
;
}
panic ("Unhandled revision type!");
return 0;
}
static enum arch_revision_type
arch_revision_type_enum (t_uchar * type)
{
if (str_cmp("import", type) == 0)
return arch_import_revision;
if (str_cmp("simple", type) == 0)
return arch_simple_revision;
if (str_cmp("continuation", type) == 0)
return arch_continuation_revision;
panic ("Unhandled revision type!");
exit (2);
}
/**
* This is a kinda freaky function, because we can only use the Arch Cache
* some of the time.
* If the archive_cached parameter is non-null, we must query the archive,
* since cacherevs may be added or deleted anytime.
* ancestry however, may be cached separately, though for now
* we trest it like archive_cached
* If we have a full tree in the cache, we force archive_cached to true
* unless it's an import revision
*/
static int
cache_revision_type (enum arch_revision_type * type, int * archive_cached,
int *has_ancestry,
struct arch_archive * a, t_uchar const * revision)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
enum arch_revision_type tmp_type;
t_uchar * type_query = arch_revision_query (a, revision, "type");
int use_archive = 0;
int cache_put = 0;
int answer = -1;
if (arch_cache_has_answer (type_query))
{
cache_put = 0;
use_archive = (archive_cached != 0 || has_ancestry != 0);
}
else
{
cache_put = 1;
use_archive = 1;
}
if (use_archive)
{
answer = wrapped_arch->vtable->revision_type (&tmp_type, archive_cached, has_ancestry, wrapped_arch, revision);
}
else
{
t_uchar * type_str = arch_cache_get_line (type_query);
tmp_type = arch_revision_type_enum (type_str);
answer = 0;
lim_free (0, type_str);
}
if (type != 0)
*type = tmp_type;
if (answer == -1)
return answer;
if (cache_put)
{
arch_cache_put_line (type_query, arch_revision_type_string (tmp_type));
}
if (archive_cached != 0 && tmp_type != arch_import_revision && !*archive_cached)
{
t_uchar * tree_query = arch_revision_query (a, revision, "full-tree.tar.gz");
if (arch_cache_has_answer (tree_query))
*archive_cached = 1;
lim_free (0, tree_query);
}
lim_free (0, type_query);
return answer;
}
static void
cache_get_patch (int out_fd, struct arch_archive * a, t_uchar * revision)
{
t_uchar * query = arch_revision_query (a, revision, "delta.tar.gz");
if (!arch_cache_has_answer(query))
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
t_uchar * tmp_name;
int put_fd = arch_cache_put (&tmp_name, query);
wrapped_arch->vtable->get_patch (put_fd, wrapped_arch, revision);
safe_close (put_fd);
arch_cache_commit (tmp_name, query);
lim_free (0, tmp_name);
}
copy_fd_close (arch_cache_get (query), out_fd);
lim_free (0, query);
}
static void
cache_get_cached (int out_fd, struct arch_archive * a, t_uchar * revision)
{
t_uchar * query = arch_revision_query (a, revision, "full-tree.tar.gz");
if (!arch_cache_has_answer(query))
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
t_uchar * tmp_name;
int put_fd = arch_cache_put (&tmp_name, query);
wrapped_arch->vtable->get_cached (put_fd, wrapped_arch, revision);
safe_close (put_fd);
arch_cache_commit (tmp_name, query);
lim_free (0, tmp_name);
}
copy_fd_close (arch_cache_get (query), out_fd);
lim_free (0, query);
}
static void
cache_get_import (int out_fd, struct arch_archive * a, t_uchar * revision)
{
t_uchar * query = arch_revision_query (a, revision, "full-tree.tar.gz");
if (!arch_cache_has_answer(query))
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
t_uchar * tmp_name;
int put_fd = arch_cache_put (&tmp_name, query);
wrapped_arch->vtable->get_import(put_fd, wrapped_arch, revision);
safe_close (put_fd);
arch_cache_commit (tmp_name, query);
lim_free (0, tmp_name);
}
copy_fd_close (arch_cache_get (query), out_fd);
lim_free (0, query);
}
static t_uchar *
cache_get_continuation (struct arch_archive * a, t_uchar * revision)
{
t_uchar * ancestor_query = arch_revision_query (a, revision, "ancestor");
t_uchar * continuation = 0;
if (arch_cache_has_answer (ancestor_query))
{
continuation = arch_cache_get_line (ancestor_query);
}
else
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
continuation = wrapped_arch->vtable->get_continuation (wrapped_arch, revision);
continuation = trim_surrounding_ws (continuation);
arch_cache_put_line (ancestor_query, continuation);
}
lim_free (0, ancestor_query);
return continuation;
}
int
cache_set_meta_info (struct arch_archive * a,
t_uchar * meta_info_name,
t_uchar * meta_info_value)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->set_meta_info (wrapped_arch, meta_info_name, meta_info_value);
}
static t_uchar *
cache_get_meta_info (struct arch_archive * a, t_uchar * meta_info_name)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->get_meta_info (wrapped_arch, meta_info_name);
}
static int cache_make_category (t_uchar ** errstr, struct arch_archive * a, t_uchar * category)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->make_category (errstr, wrapped_arch, category);
}
static int cache_make_branch (t_uchar ** errstr, struct arch_archive * a, t_uchar * branch)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->make_branch(errstr, wrapped_arch, branch);
}
static int
cache_make_version (t_uchar ** errstr, struct arch_archive * a, t_uchar * version)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->make_version(errstr, wrapped_arch, version);
}
static int
cache_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)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->lock_revision (errstr, wrapped_arch, version, prev_level, uid, txn_id, new_level);
}
static int
cache_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)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->revision_ready (errstr, wrapped_arch, from_archive, version, prev_level, uid, txn_id, new_level);
}
static int
cache_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)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->finish_revision (errstr, wrapped_arch, version, prev_level, uid, txn_id, new_level);
}
static enum arch_revision_lock_state
cache_lock_state (t_uchar ** prev_level_ret,
t_uchar ** uid_ret,
t_uchar ** txn_id_ret,
struct arch_archive * a,
t_uchar * version)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->lock_state (prev_level_ret, uid_ret, txn_id_ret, wrapped_arch, version);
}
static int
cache_break_revision_lock (t_uchar ** errstr, struct arch_archive * a,
t_uchar * version,
t_uchar * prev_level,
t_uchar * uid,
t_uchar * txn_id)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->break_revision_lock(errstr, wrapped_arch, version, prev_level, uid, txn_id);
}
static int
cache_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)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->put_log (errstr, wrapped_arch, version, prev_level, uid, txn_id, log_text);
}
static int
cache_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)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->put_continuation (errstr, wrapped_arch, version, prev_level, uid, txn_id, continuation);
}
static int
cache_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,
int in_fd)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->put_changeset (errstr, wrapped_arch, version, prev_level, uid, txn_id, level, in_fd);
}
static int
cache_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,
int in_fd)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->put_import (errstr, wrapped_arch, version, prev_level, uid, txn_id, level, in_fd);
}
static int
cache_put_cached (t_uchar ** errstr, struct arch_archive * a, struct arch_archive *from_archive,
t_uchar * revision, int in_fd)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->put_cached (errstr, wrapped_arch, from_archive, revision, in_fd);
}
static int
cache_delete_cached (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->delete_cached (errstr, wrapped_arch, revision);
}
static void
cache_repair_non_txnal (int chatter_fd, struct arch_archive * a)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
wrapped_arch->vtable->repair_non_txnal (chatter_fd, wrapped_arch);
}
void
cache_get_ancestry (int out_fd, struct arch_archive * a, t_uchar * revision)
{
/* FIXME: stash this in the cache */
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->get_ancestry (out_fd, wrapped_arch, revision);
}
static int
cache_put_ancestry (t_uchar ** errstr, struct arch_archive * a, struct arch_archive *from_archive,
t_uchar * revision, int in_fd)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->put_ancestry (errstr, wrapped_arch, from_archive, revision, in_fd);
}
static int
cache_delete_ancestry (t_uchar ** errstr, struct arch_archive * a, t_uchar * revision)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) a)->wrapped_arch;
return wrapped_arch->vtable->delete_ancestry (errstr, wrapped_arch, revision);
}
struct arch_archive *
arch_cached_archive_as_pfs(struct arch_archive * arch)
{
struct arch_archive *result;
invariant (!str_cmp (arch->vtable->type, "cache"));
result = ((struct arch_cached_archive *)arch)->wrapped_arch;
invariant (!str_cmp (result->vtable->type, "pfs"));
return result;
}
int
cache_set_mirror (t_uchar ** errstr, struct arch_archive * archive, int enabled)
{
struct arch_archive *wrapped_arch = ((struct arch_cached_archive *) archive)->wrapped_arch;
return arch_archive_set_mirror (errstr, wrapped_arch, enabled);
}
/* tag: b6a409d5-c4b2-48ee-bdb4-5c2eeebb2bc6
*/
syntax highlighted by Code2HTML, v. 0.9.1