Logo Search packages:      
Sourcecode: bazaar version File versions

inode-sig.c

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

#include "hackerlab/bugs/panic.h"
#include "hackerlab/mem/mem.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "libfsutils/ensure-dir.h"
#include "libfsutils/dir-listing.h"
#include "libfsutils/string-files.h"
#include "libawk/relational.h"
#include "libawk/relassoc.h"
#include "libarch/arch.h"
#include "libarch/namespace.h"
#include "libarch/invent.h"
#include "libarch/inode-sig.h"



#define MAX_INODE_SIG_FILES 5

static t_uchar const * INODE_IDS = "{arch}/,,inode-sigs";
static t_uchar const * INODE_PATHS = "{arch}/,,inode-sig-paths";


/* __STDC__ prototypes for static functions */
static void arch_update_inode_sig (rel_table sig, assoc_table newsig, assoc_table id_list);
static void inode_sig_callback (t_uchar * path, struct stat * stat_buf,
                    enum arch_inventory_category category,
                    t_uchar * id, int has_source_name,
                    void * closure,
                    int escape_classes);
static void arch_update_inode_sig (rel_table sig, assoc_table newsig, assoc_table id_list);
static t_uchar * arch_inode_sig_dir (t_uchar * tree_root, t_uchar const * sig_type);
static t_uchar * arch_inode_sig_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type);
static t_uchar * arch_inode_sig_tmp_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type);
static int arch_creat_inode_sig_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type);
static void arch_finish_inode_sig_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type, int fd);
static void arch_prune_inode_sig_dir (t_uchar * tree_root, t_uchar * current_inode_sig_file, t_uchar const * sig_type);
static void prune_old_ctime_cruft (rel_table the_table);
static void prune_old_dev_cruft (rel_table the_table);
static void arch_read_inode_sig (inode_sig * sig, t_uchar * tree_root, t_uchar * archive, t_uchar * revision);
static void arch_read_inode_sig_paths (rel_table * as_table, assoc_table * as_assoc,
                                       t_uchar * tree_root, t_uchar * archive, t_uchar * revision);
static void arch_write_inode_sig_file (inode_sig sig, t_uchar * tree_root, t_uchar * archive, t_uchar * revision);
static int arch_inode_sig_equal (int current_result, rel_table existing_sig, rel_table current_sig, int chatter_fd);



t_uchar *
arch_statb_inode_sig (struct stat * statb)
{
  int fd = make_output_to_string_fd ();

  safe_printfmt (fd, "ino=%lu:mtime=%lu:size=%lu",
                 (t_ulong)statb->st_ino,
                 (t_ulong)statb->st_mtime,
                 (t_ulong)statb->st_size);

  return string_fd_close (fd);
}


inode_sig
arch_tree_inode_sig (t_uchar * tree_root)
{
  struct arch_inventory_options options = {0, };
  int here_fd;
  inode_sig answer;
  
  answer.ids = 0;
  answer.paths = 0;

  here_fd = safe_open (".", O_RDONLY, 0);
  safe_chdir (tree_root);

  options.categories = arch_inventory_source;
  options.want_ids = 1;
  options.include_excluded = 1;

  arch_get_inventory_naming_conventions (&options, ".");

  arch_inventory_traversal (&options, ".", inode_sig_callback, (void *)&answer, 0);

  arch_free_inventory_naming_conventions (&options);

  rel_sort_table_by_field (0, answer.ids, 0);

  safe_fchdir (here_fd);
  safe_close (here_fd);

  return answer;
}

void
arch_snap_inode_sig (t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  inode_sig sig = arch_tree_inode_sig (tree_root);
  arch_write_inode_sig_file (sig, tree_root, archive, revision);
  arch_inode_sig_free (sig);
}

static void
arch_write_inode_sig_file (inode_sig sig, t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  int fd;
  
  fd = arch_creat_inode_sig_file (tree_root, archive, revision, INODE_IDS);
  rel_print_pika_escape_iso8859_1_table (fd, arch_escape_classes, sig.ids);
  arch_finish_inode_sig_file (tree_root, archive, revision, INODE_IDS, fd);

  fd = arch_creat_inode_sig_file (tree_root, archive, revision, INODE_PATHS);
  rel_print_pika_escape_iso8859_1_table (fd, arch_escape_classes, sig.paths);
  arch_finish_inode_sig_file (tree_root, archive, revision, INODE_PATHS, fd);
}
 
void
arch_snap_inode_sig_files (t_uchar * tree_root, t_uchar * archive, t_uchar * prev_revision, t_uchar * revision, rel_table file_list)
{
  inode_sig oldsig;
  assoc_table newsig_as;
  assoc_table newsig_paths;
  inode_sig newsig;
  assoc_table id_list;
  
  arch_read_inode_sig (&oldsig, tree_root, archive, prev_revision);
  newsig = arch_tree_inode_sig (tree_root);
  newsig_as = rel_to_assoc (newsig.ids, 0, 1);
  newsig_paths = rel_to_assoc (newsig.paths, 0, 1);

  id_list = arch_filenames_ids ( &file_list, tree_root);

  arch_update_inode_sig (oldsig.ids, newsig_as, id_list);
  arch_update_inode_sig (oldsig.paths, newsig_paths, id_list);

  arch_write_inode_sig_file (oldsig, tree_root, archive, revision);

  arch_inode_sig_free (oldsig);
  arch_inode_sig_free (newsig);
  free_assoc_table (newsig_as);
  free_assoc_table (newsig_paths);
  free_assoc_table (id_list);
}


static void 
arch_update_inode_sig (rel_table sig, assoc_table newsig, assoc_table id_list)
{
  int sig_size = rel_n_records (sig);
  int i;

  for (i = 0; i != sig_size; ++ i)
    {
      if (assoc_ref (id_list, sig [i] [0] ))
      {
        rel_record new_record = 0;
        t_uchar *vals = assoc_ref (newsig, sig [i] [0]);
        invariant (vals != 0);
        new_record = rel_make_record (sig [i] [0], vals, 0);
        rel_free_record (sig [i]);
        sig [i] = new_record;
      }
    }
}


int
arch_valid_inode_sig (t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  int result = 1;
  inode_sig current_sig = arch_tree_inode_sig (tree_root);
  
  if (safe_access (arch_inode_sig_file(tree_root, archive, revision, INODE_IDS), F_OK))
    {
      safe_printfmt (2, "Missing inode signature. Cannot verify reference tree integrity: you should remove and recreate this reference tree.\n    tree: %s\n    archive: %s\n    revision: %s\n",
      tree_root, archive, revision);
    }
   else 
    {
      rel_table existing_sig = 0;

      arch_read_inode_sig_ids (&existing_sig, NULL, tree_root, archive, revision);
      result = arch_inode_sig_equal (result, existing_sig, current_sig.ids, 2);
      rel_free_table (existing_sig);
    }
   
  if (safe_access (arch_inode_sig_file(tree_root, archive, revision, INODE_PATHS), F_OK))
    {
      /* don't spew warnings, most folk have perfectly ok revlibs. this is a 
       * correctness problem that using baz will just address over time.
       */
    }
   else 
    {
      rel_table existing_paths = 0;

      arch_read_inode_sig_paths (&existing_paths, NULL, tree_root, archive, revision);
      result = arch_inode_sig_equal (result, existing_paths, current_sig.paths, 2);
      rel_free_table (existing_paths);
    }

  arch_inode_sig_free (current_sig);
  return result;
}

static int
arch_inode_sig_equal (int current_result, rel_table existing_sig, rel_table current_sig, int chatter_fd)
{
  /* Is there a convenience function (a rel_join magic foo?) for this */
  if (rel_n_records (existing_sig) == rel_n_records (current_sig))
    {
      int counter,inner;
      for (counter = 0; counter < rel_n_records (existing_sig); ++counter)
        for (inner = 0; inner < 2; ++inner)
          if (str_cmp(existing_sig[counter][inner],current_sig[counter][inner]))
          {
            current_result = 0;
            if (chatter_fd > -1)
            safe_printfmt (chatter_fd, "File signature changed: from '%s' to '%s'\n", existing_sig[counter][inner],current_sig[counter][inner]);
          }
    }
  else current_result = 0;
  return current_result;
}

static void
arch_read_inode_sig (inode_sig * sig, t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
    arch_read_inode_sig_ids (&sig->ids, 0, tree_root, archive, revision);
    arch_read_inode_sig_paths (&sig->paths, 0, tree_root, archive, revision);
}

static void arch_read_inode_sig_paths (rel_table * as_table, assoc_table * as_assoc,
                                       t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  t_uchar * sig_file = arch_inode_sig_file (tree_root, archive, revision, INODE_PATHS);

  if (as_table)
    *as_table = 0;

  if (as_assoc)
    *as_assoc = 0;

  if (!safe_access (sig_file, F_OK))
    {
      int fd = safe_open (sig_file, O_RDONLY, 0);
      rel_table the_table;

      the_table = rel_read_pika_unescape_iso8859_1_table (fd, 2, "arch", sig_file);

      if (as_assoc)
        *as_assoc = rel_to_assoc (the_table, 0, 1);

      if (as_table)
        *as_table = the_table;
      else
        rel_free_table (the_table);

      safe_close (fd);
    }
}

void
arch_read_inode_sig_ids (rel_table * as_table, assoc_table * as_assoc,
                     t_uchar * tree_root, t_uchar * archive, t_uchar * revision)
{
  t_uchar * sig_file = arch_inode_sig_file (tree_root, archive, revision, INODE_IDS);

  if (as_table)
    *as_table = 0;

  if (as_assoc)
    *as_assoc = 0;

  if (!safe_access (sig_file, F_OK))
    {
      int fd = safe_open (sig_file, O_RDONLY, 0);
      rel_table the_table;

      the_table = rel_read_pika_unescape_iso8859_1_table (fd, 2, "arch", sig_file);

      prune_old_ctime_cruft (the_table);
      prune_old_dev_cruft (the_table);

      if (as_assoc)
        *as_assoc = rel_to_assoc (the_table, 0, 1);

      if (as_table)
        *as_table = the_table;
      else
        rel_free_table (the_table);

      safe_close (fd);
    }
}


void
arch_read_id_shortcut (assoc_table * ids_shortcut, t_uchar * tree_root)
{
  t_uchar * inode_sig_dir = arch_inode_sig_dir (tree_root, INODE_IDS);
  rel_table dir_listing = maybe_directory_files (inode_sig_dir);
  assoc_table answer = 0;
  assoc_table rejected = 0;
  int x;
  time_t newest_time = 0;
  rel_table sig_table = 0;

  for (x = 0; x < rel_n_records (dir_listing); ++x)
    {
      t_uchar * path = file_name_in_vicinity (0, inode_sig_dir, dir_listing[x][0]);
      t_uchar * pct = str_chr_index (dir_listing[x][0], '%');

      if (pct && str_cmp (".", dir_listing[x][0]) && str_cmp ("..", dir_listing[x][0]))
        {
          t_uchar * maybe_archive = str_save_n (0, dir_listing[x][0], pct - dir_listing[x][0]);
          t_uchar * maybe_revision = str_save (0, pct + 1);

          if (arch_valid_archive_name (maybe_archive) && arch_valid_package_name (maybe_revision, arch_no_archive, arch_req_patch_level, 0))
            {
            
            struct stat statb;

            safe_lstat (path, &statb);
            if (statb.st_mtime > newest_time)
            {
              newest_time = statb.st_mtime;
                  rel_free_table (sig_table);
                  arch_read_inode_sig_ids (&sig_table, 0, tree_root, maybe_archive, maybe_revision);
            }
          }
          lim_free (0, maybe_archive);
          lim_free (0, maybe_revision);
      }
      lim_free (0, path);
    }
              
  int y;
  for (y = 0; y < rel_n_records (sig_table); ++y)
    {
      t_uchar * this_id = sig_table[y][0];
      t_uchar * this_sig = sig_table[y][1];
      
      {
        t_uchar * is_rejected = assoc_ref (rejected, this_sig);
        t_uchar * already_have = assoc_ref (answer, this_sig);
        
        if (!is_rejected)
          {
            if (already_have)
            {
              if (!str_cmp (this_id, already_have))
                {
                  assoc_del (answer, this_sig);
                  assoc_set (&rejected, this_sig, "yes");
                }
            }
            else
            {
              assoc_set (&answer, this_sig, this_id);
            }
          }
      }
    }

  rel_free_table (sig_table);
  lim_free (0, inode_sig_dir);
  rel_free_table (dir_listing);
  free_assoc_table (rejected);

  *ids_shortcut = answer;
}




static void
inode_sig_callback (t_uchar * path, struct stat * stat_buf,
                    enum arch_inventory_category category,
                    t_uchar * id, int has_source_name,
                    void * closure,
                    int escape_classes)
{
  inode_sig * answer = (inode_sig *)closure;
  t_uchar * signature = arch_statb_inode_sig (stat_buf);

  if (!S_ISDIR (stat_buf->st_mode) && !S_ISLNK (stat_buf->st_mode))
    {
      rel_add_records (&answer->ids, rel_make_record (id, signature, 0), 0);
      rel_add_records (&answer->paths, rel_make_record (id, path, 0), 0);
    }
  lim_free(0, signature);
}


static t_uchar *
arch_inode_sig_dir (t_uchar * tree_root, t_uchar const * sig_type)
{
  /* FIXME: file_name_in_vicinity should be checked for const correctness */
  return file_name_in_vicinity (0, tree_root, (t_uchar *)sig_type);
}

static t_uchar *
arch_inode_sig_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type)
{
  t_uchar * inode_sig_dir = arch_inode_sig_dir (tree_root, sig_type);
  t_uchar * basename = str_alloc_cat_many (0, archive, "%", revision, str_end);
  t_uchar * answer = file_name_in_vicinity (0, inode_sig_dir, basename);

  lim_free (0, inode_sig_dir);
  lim_free (0, basename);
  return answer;
}


static t_uchar *
arch_inode_sig_tmp_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type)
{
  t_uchar * inode_sig_dir = arch_inode_sig_dir (tree_root, sig_type);
  t_uchar * basename = str_alloc_cat_many (0, ",,", archive, "%", revision, str_end);
  t_uchar * answer = file_name_in_vicinity (0, inode_sig_dir, basename);

  lim_free (0, inode_sig_dir);
  lim_free (0, basename);
  return answer;
}


static int
arch_creat_inode_sig_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type)
{
  t_uchar * inode_sig_dir = arch_inode_sig_dir (tree_root, sig_type);
  t_uchar * inode_sig_tmp_file = arch_inode_sig_tmp_file (tree_root, archive, revision, sig_type);
  int ign;
  int answer;

  ensure_directory_exists (inode_sig_dir);
  vu_unlink (&ign, inode_sig_tmp_file);
  answer = safe_open (inode_sig_tmp_file, (O_WRONLY | O_CREAT | O_EXCL), 0444);

  lim_free (0, inode_sig_dir);
  lim_free (0, inode_sig_tmp_file);

  return answer;
}

static void
arch_finish_inode_sig_file (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar const * sig_type, int fd)
{
  t_uchar * inode_sig_tmp_file = arch_inode_sig_tmp_file (tree_root, archive, revision, sig_type);
  t_uchar * inode_sig_file = arch_inode_sig_file (tree_root, archive, revision, sig_type);

  safe_close (fd);
  safe_rename (inode_sig_tmp_file, inode_sig_file);
  arch_prune_inode_sig_dir (tree_root, inode_sig_file, sig_type);

  lim_free (0, inode_sig_tmp_file);
  lim_free (0, inode_sig_file);
}


static void
arch_prune_inode_sig_dir (t_uchar * tree_root, t_uchar * current_inode_sig_file, t_uchar const * sig_type)
{
  t_uchar * inode_sig_dir = arch_inode_sig_dir (tree_root, sig_type);
  rel_table dir_listing = maybe_directory_files (inode_sig_dir);
  int dead_one = -1;
  time_t oldest_time = 0; /* nothing is older to start with */
  int n_inode_sig_files;
  int x;

  do
    {
      n_inode_sig_files = 0;

      for (x = 0; x < rel_n_records (dir_listing); ++x)
        {
          t_uchar * path = file_name_in_vicinity (0, inode_sig_dir, dir_listing[x][0]);
          t_uchar * pct = str_chr_index (dir_listing[x][0], '%');

          if (pct && str_cmp (".", dir_listing[x][0]) && str_cmp ("..", dir_listing[x][0]))
            {
              t_uchar * maybe_archive = str_save_n (0, dir_listing[x][0], pct - dir_listing[x][0]);
              t_uchar * maybe_revision = str_save (0, pct + 1);

              if (arch_valid_archive_name (maybe_archive) && arch_valid_package_name (maybe_revision, arch_no_archive, arch_req_patch_level, 0))
                {
                  struct stat statb;

                  ++n_inode_sig_files;

                  safe_lstat (path, &statb);

                  if (str_cmp (current_inode_sig_file, path) && ((dead_one < 0) || (statb.st_mtime < oldest_time)))
                    {
                      dead_one = x;
                      oldest_time = statb.st_mtime;
                    }
                }

              lim_free (0, maybe_archive);
              lim_free (0, maybe_revision);
            }

          lim_free (0, path);
        }

      if (n_inode_sig_files > MAX_INODE_SIG_FILES)
        {
          t_uchar * dead_path = file_name_in_vicinity (0, inode_sig_dir, dir_listing[dead_one][0]);

          safe_unlink (dead_path);
          --n_inode_sig_files;

          lim_free (0, dead_path);
        }


    } while (n_inode_sig_files > MAX_INODE_SIG_FILES); /* usually only one iteration expected */

  lim_free (0, inode_sig_dir);
  rel_free_table (dir_listing);
}


static void
prune_old_ctime_cruft (rel_table the_table)
{
  int x;

#undef MIN
#define MIN(A,B) (((A) < (B)) ? (A) : (B))

  for (x = 0; x < rel_n_records (the_table); ++x)
    {
      t_uchar * sig = the_table[x][1];
      t_uchar * third_colon;
      t_uchar * fourth_colon;

      third_colon = str_chr_index (sig, ':');
      if (!third_colon)
        return;
      
      third_colon = str_chr_index (third_colon + 1, ':');
      if (!third_colon)
        return;

      third_colon = str_chr_index (third_colon + 1, ':');
      if (!third_colon)
        return;

      if (str_cmp_n (third_colon + 1, MIN (5, str_length (third_colon + 1)), "ctime", (size_t)5))
        return;

      fourth_colon = str_chr_index (third_colon + 1, ':');
      if (!fourth_colon)
        return;

      mem_move (third_colon, fourth_colon, 1 + str_length (fourth_colon));
    }
}

void 
arch_inode_sig_free (inode_sig sig)
{
  rel_free_table (sig.ids);
  rel_free_table (sig.paths);
}

static void
prune_old_dev_cruft (rel_table the_table)
{
  int x;

#undef MIN
#define MIN(A,B) (((A) < (B)) ? (A) : (B))

  for (x = 0; x < rel_n_records (the_table); ++x)
    {
      t_uchar * sig = the_table[x][1];
      t_uchar * first_colon;

      if (str_cmp_n (sig, MIN (3, str_length (sig)), "dev", (size_t)3))
        return;

      first_colon = str_chr_index (sig, ':');
      if (!first_colon)
        return;

      mem_move (sig, first_colon + 1, str_length (first_colon));
    }
}





/* tag: Tom Lord Fri Sep 12 09:56:44 2003 (inode-sig.c)
 */

Generated by  Doxygen 1.6.0   Back to index