/* 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 */
syntax highlighted by Code2HTML, v. 0.9.1