/* 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 */