/* 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 "); 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) */