Logo Search packages:      
Sourcecode: bazaar version File versions

pfs-signatures.c

/* pfs-signatures.c: 
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "config-include/config-options.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/sys/types.h"
#include "hackerlab/os/sys/wait.h"
#include "hackerlab/os/signal.h"
#include "hackerlab/char/str.h"
#include "hackerlab/hash/md5-utils.h"
#include "hackerlab/hash/sha1-utils.h"
#include "hackerlab/rx-posix/regex.h"
#include "hackerlab/vu/safe.h"
#include "hackerlab/fs/file-names.h"
#include "libfsutils/file-contents.h"
#include "libfsutils/tmp-files.h"
#include "libawk/associative.h"
#include "libawk/trim.h"
#include "libarch/my.h"
#include "libarch/namespace.h"
#include "libarch/archives.h"
#include "libarch/cached-archive.h"
#include "libarch/pfs-signatures.h"


/* __STDC__ prototypes for static functions */
static void exec_shell_command (t_uchar * command);
static t_uchar * archive_signing_rule_file (t_uchar * archive, int no_default);
static t_uchar * archive_signature_checking_rule_file (t_uchar * archive, int no_default, int signed_archive);
static int arch_pfs_memoize_checksum_file (t_uchar * archive, t_uchar * official_archive, t_uchar * revision, t_uchar * file_contents);



int
arch_pfs_has_signing_rule (t_uchar * archive)
{
  t_uchar * rule_file = 0;
  int answer = 0;

  rule_file = archive_signing_rule_file (archive, 1);
  answer = !!rule_file;

  lim_free (0, rule_file);
  return answer;
}



int
arch_pfs_sign_for_archive (t_uchar * archive, t_uchar * official_archive, t_uchar * revision, t_uchar * sigfile, int in_fd, int out_fd)
{
  t_uchar * rule_file = 0;
  t_uchar * rule = 0;
  int pid;
  int answer = 0;

  rule_file = archive_signing_rule_file (archive, 0);

  if (!rule_file)
    {
      safe_printfmt (2, ("\n"
                         "\n"
                         "********************************\n"
                         "SIGNATURE DEMANDED FOR ARCHIVE\n"
                         "  %s\n"
                         "BUT NO RULE PROVIDED\n"
                         "\n"
                         "Consider creating ~/.arch-params/signing/%s\n"
                         " or ~/.arch-params/signing/=default\n"
                         "\n"
                         "********************************\n"
                         "\n"
                         "\n"),
                     archive, archive);

    generic_error_exit:

      safe_printfmt (2, ("\n"
                         "(You may also have to use tla lock-revision -b before\n"
                         " retrying this transaction.  See tla lock-revision -H)\n"
                         "\n"));
      answer = -1;

    generic_return:

      lim_free (0, rule_file);
      lim_free (0, rule);

      return answer;
    }

  rule = file_contents (rule_file);
  rule = trim_surrounding_ws (rule);

  if (arch_valid_archive_name (rule))
    {
      /* Copy signature from another archive. */
      struct arch_archive * sig_from = 0;
      struct arch_pfs_archive * pfs_sig_from;
      t_uchar * revdir_from = 0;
      t_uchar * sigpath = 0;
      t_uchar * signed_checksum = 0;

      sig_from = arch_archive_connect (rule, 0);

      revdir_from = arch_fs_archive_revision_path (sig_from, 0, revision);
      sigpath = file_name_in_vicinity (0, revdir_from, sigfile);

      /* EVIL. GARH */
      if (!str_cmp (sig_from->vtable->type, "pfs"))
        pfs_sig_from = (struct arch_pfs_archive *)sig_from;
      else if (!str_cmp (sig_from->vtable->type, "cache"))
        pfs_sig_from = (struct arch_pfs_archive *)(arch_cached_archive_as_pfs(sig_from));
      else
        {
          safe_printfmt (2, ("\n"
                             "\n"
                             "********************************\n"
                             "\n"
                             "ARCHIVE SIGNING RULE SAYS TO COPY\n"
                             "CHECKSUMS FROM %s\n"
                             "\n"
                             "SURPRISINGLY, ARCH DOESN'T KNOW\n"
                             "HOW TO COPY FROM THAT ARCHIVE.\n"
                             "\n"
                             "  your rule file: %s\n"
                       "  archive type: %s\n"
                             "\n"
                             "********************************\n"
                             "\n"
                             "\n"),
                         rule, rule_file, sig_from->vtable->type);
          exit (2);
        }

      signed_checksum = arch_pfs_file_contents (pfs_sig_from->pfs, sigpath, 1);

      if (!signed_checksum || arch_pfs_memoize_checksum_file (archive, official_archive, revision, signed_checksum))
        {
          safe_printfmt (2, ("\n"
                             "\n"
                             "********************************\n"
                             "\n"
                             "ARCHIVE SIGNING RULE SAYS TO COPY\n"
                             "CHECKSUMS FROM %s\n"
                             "\n"
                             "THE CHECKSUM FILE IN THAT ARCHIVE FOR\n"
                             "\n"
                             "  %s\n"
                             "\n"
                             "IS MISSING OR INAPPROPRIATE.\n"
                             "\n"
                             "  your rule file: %s\n"
                             "\n"
                             "********************************\n"
                             "\n"
                             "\n"),
                         rule, revision, rule_file);
          exit (2);
        }

      safe_write_retry (out_fd, signed_checksum, str_length (signed_checksum));

      arch_archive_close (sig_from);
      lim_free (0, signed_checksum);
      lim_free (0, revdir_from);
      lim_free (0, sigpath);
      return 0;
    }
  else
    {
      /* Generate the signature anew */

      pid = fork ();

      if (pid == -1)
        {
          safe_printfmt (2, "unable to fork to run %s\n", rule);
          goto generic_error_exit;
        }

      if (pid)
        {
          int status;
          int wait_pid;

          wait_pid = waitpid (pid, &status, 0);
          if (wait_pid < 0)
            {
              panic_msg ("error waiting for signature generation subprocess");
              kill (0, SIGKILL);
              panic ("error waiting for subprocess");
            }
          if (WIFSIGNALED (status))
            {
              safe_printfmt (2, "\n");
              safe_printfmt (2, "signature subprocess killed by signal %d\n", WTERMSIG (status));
              safe_printfmt (2, "\n");
              goto generic_error_exit;
            }
          else if (!WIFEXITED (status))
            {
              panic_msg ("waitpid returned for a non-exited process");
              kill (0, SIGKILL);
              panic ("waitpid returned for a non-exited process");
            }
          else
            {
              int exit_status;

              exit_status = WEXITSTATUS (status);

              if (exit_status)
                {
                  safe_printfmt (2, ("signature command exited with non-0 status (%d)\n"
                                     "\n"
                                     "command: %s\n"
                                     "\n"),
                                 exit_status,
                                 rule);

                  goto generic_error_exit;
                }
              else
                goto generic_return;
            }
        }
      else
        {
          safe_move_fd (in_fd, 0);
          safe_move_fd (out_fd, 1);

          exec_shell_command (rule);

          panic ("not reached");
        }

      panic ("not reached");
      kill (0, SIGKILL);
      return -1;
    }
}


int
arch_pfs_check_signature_for_archive (t_uchar * archive, t_uchar * signed_message, int signed_archive)
{
  t_uchar * rule_file = 0;
  t_uchar * rule = 0;
  t_uchar * tmp_path = 0;
  int tmp_fd = -1;
  int pid;
  int answer = 0;

  rule_file = archive_signature_checking_rule_file (archive, 0, signed_archive);

  if (!rule_file)
    {
    generic_return:

      lim_free (0, rule_file);
      lim_free (0, rule);
      lim_free (0, tmp_path);
      if (tmp_fd > 0)
        safe_close (tmp_fd);

      return answer;
    }

  tmp_path = tmp_file_name ("/tmp", "checksum-contents");
  tmp_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0);
  safe_unlink (tmp_path);
  if (signed_message)
    safe_write_retry (tmp_fd, signed_message, str_length (signed_message));
  safe_lseek (tmp_fd, (off_t)0, SEEK_SET);

  rule = file_contents (rule_file);

  pid = fork ();

  if (pid == -1)
    {
      safe_printfmt (2, "unable to fork to run %s\n", rule);
      answer = -1;
      goto generic_return;
    }

  if (pid)
    {
      int status;
      int wait_pid;

      wait_pid = waitpid (pid, &status, 0);
      if (wait_pid < 0)
        {
          panic_msg ("error waiting for signature checking subprocess");
          kill (0, SIGKILL);
          panic ("error waiting for subprocess");
        }
      if (WIFSIGNALED (status))
        {
          safe_printfmt (2, "\n");
          safe_printfmt (2, "signature checking subprocess killed by signal %d\n", WTERMSIG (status));
          safe_printfmt (2, "\n");
          answer = -1;
          goto generic_return;
        }
      else if (!WIFEXITED (status))
        {
          panic_msg ("waitpid returned for a non-exited process");
          kill (0, SIGKILL);
          panic ("waitpid returned for a non-exited process");
        }
      else
        {
          int exit_status;

          exit_status = WEXITSTATUS (status);

          if (exit_status)
            answer = -1;

          goto generic_return;
        }
    }
  else
    {
      safe_move_fd (tmp_fd, 0);
      
      exec_shell_command (rule);

      panic ("not reached");
    }

  panic ("not reached");
  kill (0, SIGKILL);
  return -1;
}



static void
exec_shell_command (t_uchar * command)
{
  char * argv[5];

  argv[0] = "sh";
  argv[1] = "-e";
  argv[2] = "-c";
  argv[3] = (char *)command;
  argv[4] = 0;

  execv (cfg__posix_shell, argv);

  panic ("not reached");
}


static t_uchar * 
archive_signing_rule_file (t_uchar * archive, int no_default)
{
  t_uchar * home;
  t_uchar * params_dir = 0;
  t_uchar * rules_dir = 0;
  t_uchar * answer = 0;

  home = getenv ("HOME");
  invariant (!!home);

  params_dir = arch_my_arch_params ();
  rules_dir = file_name_in_vicinity (0, params_dir, "signing");
  
  answer = file_name_in_vicinity (0, rules_dir, archive);
  
  if (safe_access (answer, F_OK))
    {
      lim_free (0, answer);
      answer = 0;

      if (!no_default)
        {
          answer = file_name_in_vicinity (0, rules_dir, "=default");

          if (safe_access (answer, F_OK))
            {
              lim_free (0, answer);
              answer = 0;
            }
        }
    }


  lim_free (0, params_dir);
  lim_free (0, rules_dir);

  return answer;
}


static t_uchar *
archive_signature_checking_rule_file (t_uchar * archive, int no_default, int signed_archive)
{
  static assoc_table already_checked = 0;
  static assoc_table already_know = 0;

  t_uchar * check_rule_name = 0;
  t_uchar * home;
  t_uchar * params_dir = 0;
  t_uchar * rules_dir = 0;
  t_uchar * answer = 0;

  answer = assoc_ref (already_know, archive);

  if (answer)
    {
      answer = str_save (0, answer);
    }
  else if (!assoc_ref (already_checked, archive))
    {
      if (signed_archive)
        check_rule_name = str_alloc_cat (0, archive, ".check");
      else
        check_rule_name = str_alloc_cat (0, archive, ".unsigned-check");

      home = getenv ("HOME");
      invariant (!!home);
      
      params_dir = arch_my_arch_params ();
      rules_dir = file_name_in_vicinity (0, params_dir, "signing");
      
      answer = file_name_in_vicinity (0, rules_dir, check_rule_name);
  
      if (safe_access (answer, F_OK))
        {
          lim_free (0, answer);

          if (no_default)
            answer = 0;
          else
            {
              if (signed_archive)
                answer = file_name_in_vicinity (0, rules_dir, "=default.check");
              else
                answer = file_name_in_vicinity (0, rules_dir, "=default.unsigned-check");

              if (safe_access (answer, F_OK))
                {
                  lim_free (0, answer);
                  answer = 0;
                }
            }
        }

      if (answer || !no_default)
        {
          assoc_set (&already_checked, archive, "yes");
          if (answer)
            {
              assoc_set (&already_know, archive, answer);
            }
          else if (signed_archive)
            {
              safe_printfmt (2, ("\n"
                                 "WARNING: no rule found for checking signatures from %s\n"
                                 "\n"
                                 "  Consider creating ~/.arch-params/signing/%s.check\n"
                                 "  or ~/.arch-params/signing/=default.check\n"
                                 "\n"),
                             archive, archive);
            }
        }
    }

  lim_free (0, check_rule_name);
  lim_free (0, params_dir);
  lim_free (0, rules_dir);

  return answer;
}




static assoc_table remembered_md5s = 0;
/*
 * keys are
 *          $archive $revision $file
 * 
 * values are (ascii, hex) md5 checksums.
 */

static assoc_table remembered_sha1s = 0;
/*
 * keys are
 *          $archive $revision $file
 * 
 * values are (ascii, hex) SHA1 checksums.
 */


static assoc_table checked_revisions = 0;
/*
 * keys are
 *          $archive $revision
 * 
 * values are "yes" meaning that the checksum
 * file for that revision has been read.
 */


static assoc_table checksummed_revisions = 0;
/*
 * keys are
 *          $archive $revision
 * 
 * values are "yes" meaning that a checksum
 * file for that revision was found.
 */


void
arch_pfs_invalidate_checksum_data (struct arch_pfs_archive * arch, t_uchar * revision)
{
  t_uchar *revision_key = str_alloc_cat_many (0, arch->arch.name, " ", revision, str_end);
  if (assoc_ref (checked_revisions, revision_key))
      assoc_del (checked_revisions, revision_key);
}

int
arch_pfs_ensure_checksum_data (struct arch_pfs_archive * arch, t_uchar * revision)
{
  int answer = 0;
  t_uchar * revision_key = 0;

  revision_key = str_alloc_cat_many (0, arch->arch.name, " ", revision, str_end);

  if (!assoc_ref (checked_revisions, revision_key))
    {
      t_uchar * revision_dir = 0;
#     define N_CHECKSUM_FILES 3
      static char * checksum_file_name[N_CHECKSUM_FILES] = { "checksum", "checksum.cacherev", "ancestry.gz.checksum" };
      int x;
      t_uchar * check_rule = 0;
      int found_checksums = 0;

      /* If the user thinks he wants to signature-check this archive,
       * but the archive doesn't think it's signed, then freak out.
       */
      check_rule = archive_signature_checking_rule_file (arch->arch.name, 1, 1);

      if (check_rule && !arch->arch.signed_archive)
        {
          safe_printfmt (2, ("\n"
                             "\n"
                             "********************************\n"
                             "\n"
                             "ARCHIVE NOT SIGNED BUT YOU HAVE A RULE\n"
                             "FOR SIGNATURE CHECKING THIS ARCHIVE.\n"
                             "\n"
                             "  archive: %s\n"
                             "  revision %s\n"
                             "\n"
                             "  your rule file: %s\n"
                             "\n"
                             "********************************\n"
                             "\n"
                             "\n"),
                         arch->arch.name, revision, check_rule);
          answer = -1;
        }

      revision_dir = arch_fs_archive_revision_path (&arch->arch, 0, revision);

      for (x = 0; x < N_CHECKSUM_FILES; ++x)
        {
          t_uchar * file_path = 0;
          t_uchar * file_contents = 0;

          file_path = file_name_in_vicinity (0, revision_dir, checksum_file_name[x]);
          file_contents = arch_pfs_file_contents (arch->pfs, file_path, 1);

          if (file_contents)
            {
              assoc_set (&checksummed_revisions, revision_key, "yes");
              found_checksums = 1;
            }

          if ((file_contents || !arch->arch.signed_archive) && arch_pfs_check_signature_for_archive (arch->arch.name, file_contents, arch->arch.signed_archive))
            {
              safe_printfmt (2, ("\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"
                                 "INVALID SIGNATURE ON REVISION!\n"
                                 "  archive: %s\n"
                                 "  revision %s\n"
                                 "  checksum file: %s\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"
                                 "\n"),
                             arch->arch.name, revision, checksum_file_name[x]);
              answer = -1;
            }
          else if (file_contents && arch_pfs_memoize_checksum_file (arch->arch.name, arch->arch.official_name, revision, file_contents))
            {
              safe_printfmt (2, ("\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"
                                 "INVALID CHECKSUM FILE SYNTAX FOR REVISION!\n"
                                 "  archive: %s\n"
                                 "  revision %s\n"
                                 "  checksum file: %s\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"
                                 "\n"),
                             arch->arch.name, revision, checksum_file_name[x]);
              answer = -1;
            }

          lim_free (0, file_path);
          lim_free (0, file_contents);
        }

      assoc_set (&checked_revisions, revision_key, "yes");

      /* If the archive is supposed to be signed, but no checksums 
       * were found, fail.   If the archive is not signed, but no 
       * checksums were found, warn.
       */

      if (!found_checksums)
        {
          if (arch->arch.signed_archive)
            {
              safe_printfmt (2, ("\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"
                                 "NO CHECKSUMS FOUND FOR REVISION!\n"
                                 " IN ARCHIVE THAT'S SUPPOSED TO\n"
                                 " BE SIGNED!\n"
                                 "\n"
                                 "  archive: %s\n"
                                 "  revision %s\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"
                                 "\n"),
                             arch->arch.name, revision);
              answer = -1;
            }
          else
            {
              safe_printfmt (2, ("\n"
                                 "********************************\n"
                                 "NO CHECKSUMS FOUND FOR REVISION\n"
                                 " (unsigned archive, continuing anyway)\n"
                                 "\n"
                                 "  archive: %s\n"
                                 "  revision %s\n"
                                 "\n"
                                 "********************************\n"
                                 "\n"),
                             arch->arch.name, revision);
            }
        }
    
      lim_free (0, revision_dir);
      lim_free (0, check_rule);
    }

  lim_free (0, revision_key);

  return answer;
}

static void
arch_pfs_insert_key (t_uchar *line, t_uchar *key_prefix, regmatch_t *file, regmatch_t *hash, assoc_table *table)
{
  t_uchar * file_name = 0;
  t_uchar * assoc_key = 0;
  t_uchar * val = 0;
  
  file_name = str_save_n (0, line + file->rm_so, file->rm_eo - file->rm_so);
  assoc_key = str_alloc_cat (0, key_prefix, file_name);
  val = str_save_n (0, line + hash->rm_so, hash->rm_eo - hash->rm_so);
  
  assoc_set (table, assoc_key, val);
  
  lim_free (0, file_name);
  lim_free (0, assoc_key);
  lim_free (0, val);
}

static int
arch_pfs_memoize_checksum_file (t_uchar * archive, t_uchar * official_archive, t_uchar * revision, t_uchar * file_contents)
{
  static char file_head_pattern[] = "^Signature-for: ([^/]+)/(.+)$";
  static char md5_pattern[] = "^md5 ([^\n ]+) ([a-f0-9]+)";
  static char sha1_pattern[] = "^sha1 ([^\n ]+) ([a-f0-9]+)";
  static int is_compiled = 0;
  static regex_t file_head_preg;
  static regex_t md5_preg;
  static regex_t sha1_preg;

  regmatch_t pmatch[3];

  if (!is_compiled)
    {
      if (regcomp (&file_head_preg, file_head_pattern, REG_EXTENDED | REG_NEWLINE))
        panic ("unable to compile checksum-file head regexp");
      if (regcomp (&md5_preg, md5_pattern, REG_EXTENDED))
        panic ("unable to compile checksum-file MD5 regexp");
      if (regcomp (&sha1_preg, sha1_pattern, REG_EXTENDED))
        panic ("unable to compile checksum-file SHA1 regexp");
      is_compiled = 1;
    }

  if (regexec (&file_head_preg, file_contents, 3, pmatch, 0))
    return -1;

  if (str_cmp_n (official_archive, str_length (official_archive),
                 file_contents + pmatch[1].rm_so, (pmatch[1].rm_eo - pmatch[1].rm_so)))
    return -1;

  if (str_cmp_n (revision, str_length (revision),
                 file_contents + pmatch[2].rm_so, (pmatch[2].rm_eo - pmatch[2].rm_so)))
    return -1;


  {
    t_uchar * key_prefix = 0;
    
    key_prefix = str_alloc_cat_many (0, archive, " ", revision, " ", str_end);

    file_contents = str_chr_index (file_contents, '\n');
    if (file_contents)
      file_contents++;
  
    while (file_contents && *file_contents)
      {
      if (!regexec (&md5_preg, file_contents, 3, pmatch, 0))
        arch_pfs_insert_key (file_contents, key_prefix, &pmatch[1], &pmatch[2], &remembered_md5s);
      else if (!regexec (&sha1_preg, file_contents, 3, pmatch, 0))
        arch_pfs_insert_key (file_contents, key_prefix, &pmatch[1], &pmatch[2], &remembered_sha1s);
      else
        ; /*
           * Ignore this line for now; it may be another hash or other metadata
           * that was added by a future tla version.
           */
        
      file_contents = str_chr_index (file_contents, '\n');
      if (file_contents)
        file_contents++;
      }
      
    lim_free (0, key_prefix);
  }

  return 0;
}

int
arch_pfs_checksum_anticipates_file (t_uchar * archive, t_uchar * revision, t_uchar * file)
{
  t_uchar * key = 0;
  int answer;

  key = str_alloc_cat_many (0, archive, " ", revision, " ", file, str_end);

  /*
   * Only MD5 is required for now.  Perhaps at a later point we will
   * also require SHA1.
   */
  answer = !!assoc_ref (remembered_md5s, key);

  lim_free (0, key);
  return answer;
}


int
arch_pfs_checksum_governs (t_uchar * archive, t_uchar * revision)
{
  t_uchar * key = 0;
  int answer;

#undef FIXME
  /* Under what circumstance should it be an error that a revision
   * lacks a checksum file?   (*_ensure_* abover should handle this)
   */

  key = str_alloc_cat_many (0, archive, " ", revision, str_end);

  answer = !!assoc_ref (checksummed_revisions, key);

  lim_free (0, key);
  return answer;
}


int
arch_pfs_checksum_governs_strictly (struct arch_pfs_archive * arch)
{
  return arch->arch.signed_archive;
}


static void
arch_pfs_validate_checksums (t_uchar *archname, t_uchar *revision, t_uchar *tail, t_uchar *key, t_uchar *contents_md5, t_uchar *contents_sha1)
{
      t_uchar * remembered_md5 = 0;
      t_uchar * remembered_sha1 = 0;

      remembered_md5 = assoc_ref (remembered_md5s, key);
      remembered_sha1 = assoc_ref (remembered_sha1s, key);

      if ((remembered_md5 && str_cmp (remembered_md5, contents_md5))
        || (remembered_sha1 && str_cmp (remembered_sha1, contents_sha1)))
        {
          safe_printfmt (2, ("\n"
                             "********************************\n"
                             " MISMATCHED ARCHIVE CHECKSUM\n"
                             "\n"
                             "  archive: %s\n"
                             "  revision: %s\n"
                             "  file: %s\n"
                             "\n"
                             "  expected:\n"), archname, revision, tail);

        if (remembered_md5)
          safe_printfmt (2, (" MD5: %s\n"), remembered_md5);
        if (remembered_sha1)
          safe_printfmt (2, (" SHA1: %s\n"), remembered_sha1);
        safe_printfmt (2, ("  got:\n"));
        if (remembered_md5)
          safe_printfmt (2, (" MD5: %s\n"), contents_md5);
        if (remembered_sha1)
          safe_printfmt (2, (" SHA1: %s\n"), contents_sha1);
        safe_printfmt (2, ("\n"
                             "********************************\n"
                             "\n"));
          exit (2);
        }
}

t_uchar *
arch_pfs_checked_file_contents (struct arch_pfs_archive * arch, t_uchar * revision, t_uchar * path)
{
  if (!arch_pfs_checksum_governs (arch->arch.name, revision))
    return arch_pfs_file_contents (arch->pfs, path, 0);
  else
    {
      t_uchar * tail = 0;
      t_uchar * key = 0;
      t_uchar * contents = 0;
      t_uchar * contents_md5 = 0;
      t_uchar * contents_sha1 = 0;

      tail = file_name_tail (0, path);
      key = str_alloc_cat_many (0, arch->arch.name, " ", revision, " ", tail, str_end);

      contents = arch_pfs_file_contents (arch->pfs, path, 0);
      contents_md5 = md5_ascii_for_str (0, contents);
      contents_sha1 = sha1_ascii_for_str (0, contents);

      arch_pfs_validate_checksums (arch->arch.name, revision, tail, key, contents_md5, contents_sha1);

      lim_free (0, tail);
      lim_free (0, key);
      lim_free (0, contents_md5);
      lim_free (0, contents_sha1);

      return contents;
    }
}


void
arch_pfs_checked_get_file (struct arch_pfs_archive * arch, t_uchar * revision, int out_fd, t_uchar * path)
{
  if (!arch_pfs_checksum_governs (arch->arch.name, revision))
    arch_pfs_get_file (arch->pfs, out_fd, path, 0);
  else
    {
      t_uchar * tail = 0;
      t_uchar * key = 0;
      t_uchar * tmp_path = 0;
      int tmp_fd = -1;
      t_uchar buf[4096];
      md5_context_t md5c = 0;
      sha1_context_t sha1c = 0;
      t_uchar computed_md5_raw[16];
      t_uchar computed_sha1_raw[20];
      t_uchar * computed_md5 = 0;
      t_uchar * computed_sha1 = 0;
      
      tail = file_name_tail (0, path);
      key = str_alloc_cat_many (0, arch->arch.name, " ", revision, " ", tail, str_end);

      tmp_path = tmp_file_name ("/tmp", "arch-file");
      tmp_fd = safe_open (tmp_path, O_RDWR | O_CREAT | O_EXCL, 0);
      safe_unlink (tmp_path);
      arch_pfs_get_file (arch->pfs, tmp_fd, path, 0);
      safe_lseek (tmp_fd, (off_t)0, SEEK_SET);

      md5c = make_md5_context (0);
      sha1c = make_sha1_context (0);

      while (1)
        {
          ssize_t amt;

          amt = safe_read_retry (tmp_fd, buf, sizeof (buf));

          if (!amt)
            break;
          else
            {
              md5_scan (md5c, buf, (size_t)amt);
              sha1_scan (sha1c, buf, (size_t)amt);
              safe_write_retry (out_fd, buf, (size_t)amt);
            }
        }

      md5_final (computed_md5_raw, md5c);
      sha1_final (computed_sha1_raw, sha1c);
      computed_md5 = md5_alloc_ascii (0, computed_md5_raw);
      computed_sha1 = sha1_alloc_ascii (0, computed_sha1_raw);

      arch_pfs_validate_checksums (arch->arch.name, revision, tail, key, computed_md5, computed_sha1);

      lim_free (0, tail);
      lim_free (0, key);
      lim_free (0, tmp_path);
      safe_close (tmp_fd);
      free_md5_context (0, md5c);
      lim_free (0, computed_md5);
    }
}



/* tag: Tom Lord Wed Dec 24 19:41:23 2003 (pfs-signatures.c)
 */

Generated by  Doxygen 1.6.0   Back to index