/* arch-cache.c: * **************************************************************** * Copyright (C) 2004 Aaron Bentley * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ /** * \file libarch/arch-cache.c * \brief Functions for manipulating the Arch Cache * \see cached-archive.c for an archive implementation that uses the * Arch Cache */ /** * \defgroup archcache Arch Cache manipulation functionality * @{ */ #include "hackerlab/vu/vu.h" #include "hackerlab/vu/safe.h" #include "hackerlab/bugs/exception.h" #include "hackerlab/bugs/panic.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/char/str.h" #include "hackerlab/mem/talloc.h" #include "libfsutils/copy-file.h" #include "libfsutils/ensure-dir.h" #include "libfsutils/tmp-files.h" #include "libarch/namespace.h" #include "libarch/arch-cache.h" #include "libarch/my.h" static t_uchar * full_query_path (t_uchar * rel_query_path); /** * \brief Determine whether the cache is active * \return true if the cache is active, false if it is not active */ extern int arch_cache_active (void) { return arch_my_cache_path() != NULL; } /** * \brief Convert a fully-qualified revision name into a base path * \param fq_rvsn A fully-qualified revision name (patch-id) * \return a new-allocated string */ extern t_uchar * arch_cache_revision_path (t_uchar * fq_rvsn) { invariant (arch_valid_package_name (fq_rvsn, arch_req_archive, arch_req_patch_level, 0)); return str_alloc_cat(0, "/archives/", fq_rvsn); } /** * \brief Convert a relative query path into a full filesystem path * \param a cache query string * \return a newly-allocated string */ static t_uchar * full_query_path (t_uchar * rel_query_path) { t_uchar const * cache_path = arch_my_cache_path (); if (cache_path == 0) panic("Cannot use cache; no cache path set"); return str_alloc_cat (0, cache_path, rel_query_path); } /** * \brief Determine whether the Arch Cache has an answer to the query * \param rel_query_path The cache query path to get the answer for * \return true if the cache has an answer, false if it does not (or if the * cache is disabled). */ extern int arch_cache_has_answer (t_uchar * rel_query_path) { struct stat unused; int errn; int status; t_uchar * full_path = 0; if (!arch_cache_active()) return 0; full_path = full_query_path (rel_query_path); status = vu_stat (&errn, full_path, &unused); lim_free (0, full_path); return (status == 0); } /** * \brief Produce an fd containing the answer to a query from the Arch Cache * \param rel_query_path The cache query path to get the answer for * \return fd, or -1 if the cache has no answer to the query */ extern int arch_cache_maybe_get (t_uchar * rel_query_path) { if (!arch_cache_has_answer (rel_query_path)) return -1; else return arch_cache_get (rel_query_path); } /** * \brief Return an fd containing the answer to a query from the Arch Cache * \param rel_query_path The query to get the answer for * \return a file descriptor to get the answer from. */ extern int arch_cache_get (t_uchar * rel_query_path) { t_uchar * full_path = full_query_path (rel_query_path); int fd = 0; if (rel_query_path == 0) return -1; fd = safe_open (full_path, O_RDONLY, 0); lim_free (0, full_path); return fd; } /** * \brief Produce a string containing the answer to the query * \param rel_query_path The query to get the answer for * \return a newly-allocated string, or NULL if the cache is disabled */ extern t_uchar * arch_cache_get_str (t_uchar * rel_query_path) { int err = 0; t_uchar * buf = 0; t_uchar * answer = 0; size_t len = 0; int fd = 0; if (rel_query_path == 0) return 0; fd = arch_cache_get (rel_query_path); invariant (vu_file_to_string (&err, &buf, &len, fd) != -1); answer = str_save_n (0, buf, len); lim_free (0, buf); vu_close(&err, fd); return answer; } /** * \brief Produce a string containing the answer to the query. * * A terminating newline will be clipped. * \param rel_query_path The query to get the answer for * \return a newly-allocated string, or NULL if the cache is disabled */ extern t_uchar * arch_cache_get_line (t_uchar * rel_query_path) { t_uchar * line = arch_cache_get_str(rel_query_path); int length = 0; if (line == 0) return 0; length = str_length (line); invariant (length > 0 && line[length -1] == '\n'); line[length-1] = 0; invariant (str_chr_index (line, '\n') == NULL); return line; } /** * \brief Assign an answer to the Arch Cache * * \note arch_cache_commit must be invoked to finalize the answer * \param tmp_name the temporary name of the file * \param rel_query_path: The query this answer is for * \return the fd to write the answer to */ extern int arch_cache_put (t_uchar **tmp_name, t_uchar *rel_query_path) { t_uchar * full_path = full_query_path (rel_query_path); t_uchar * dir_path = file_name_directory_file (0, full_path); int fd = -1; if (rel_query_path != 0) { t_uchar *temp = talloc_tmp_file_name (talloc_context, dir_path, ",,temp"); *tmp_name = str_save (0, temp); talloc_free (temp); ensure_directory_exists (dir_path); fd = safe_open (*tmp_name, O_WRONLY | O_CREAT | O_EXCL, 0666); } else { *tmp_name = 0; fd = safe_open ("/dev/null", O_WRONLY, 0666); } lim_free (0, full_path); lim_free (0, dir_path); return fd; } /** * \brief Finalize a storing a cache answer. * * Any previous value will be overwritten. This is an atomic operation. * \param tmp_name The temporary filename that was written to * \param rel_query_path The cache query path to store the answer in */ extern void arch_cache_commit (t_uchar *tmp_name, t_uchar *rel_query_path) { t_uchar * full_path = 0; if (rel_query_path == 0) return; full_path = full_query_path (rel_query_path); safe_rename (tmp_name, full_path); lim_free (0, full_path); } /** * \brief Assign a string as the answer to the cache query. * * No commit necessary. * \param rel_query_path The cache query path to store data for * \param answer The query answer to store */ extern void arch_cache_put_str (t_uchar * rel_query_path, t_uchar * answer) { t_uchar * tmp_name; int fd = 0; if (rel_query_path == 0) return; fd = arch_cache_put (&tmp_name, rel_query_path); safe_write (fd, answer, str_length (answer)); safe_close (fd); arch_cache_commit (tmp_name, rel_query_path); } /** * \brief Assign a string as the answer to the cache query, appending a newline. * * No commit necessary. * \param rel_query_path The cache query path to store data for * \param answer The query answer to store */ extern void arch_cache_put_line (t_uchar * rel_query_path, t_uchar const * answer) { t_uchar * tmp_name; int fd = 0; invariant (str_chr_index (answer, '\n') == NULL); if (rel_query_path == 0) return; fd = arch_cache_put (&tmp_name, rel_query_path); safe_write (fd, (t_uchar *)answer, str_length (answer)); safe_write (fd, "\n", 1); safe_close (fd); arch_cache_commit (tmp_name, rel_query_path); } /** * Create an appropriate extended arch query */ t_uchar * arch_cache_query_new (arch_patch_id *patch_id, t_uchar const * extension) { t_uchar * base_query = 0; t_uchar * query = 0; if (!arch_cache_active ()) return NULL; base_query = arch_cache_revision_path (arch_patch_id_patch_id(patch_id)); query = str_alloc_cat_many (0, base_query, "/", extension, str_end); lim_free (0, base_query); return query; } /* tag: 92436b23-1e43-46bc-ad33-8f758d440775 */