Logo Search packages:      
Sourcecode: dctc version File versions  Download package

md_db.c

/* DChub - a Direct Connect Hub for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * md_db.c: Copyright (C) Eric Prevoteau <www@a2pb.gotdns.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
/*
$Id: md_db.c,v 1.7 2003/12/28 08:12:38 uid68112 Exp $
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef WITH_GCRYPT
#include <gcrypt.h>
#define MD4_DIGEST_LENGTH 16
#define MD4_ALGO(source,len,dest)   gcry_md_hash_buffer(GCRY_MD_MD4,dest,source,len)
#else
#include <openssl/md4.h>
#define MD4_ALGO MD4
#endif
#include <string.h>
#include <glib.h>

#include "md_db.h"
#include "lmp.h"
#include "toolkit.h"
#include "md_crc.h"

#define PREALLOC_CONST 10000
#define PREALLOC_VAR (PREALLOC_CONST*64)
#define MD_BLOC_SIZE MD4_DIGEST_LENGTH

/***************************************************************************************************/
/* the MD db uses 2 files, one for data with a constant size and one for data with a variable size */
/***************************************************************************************************/
static gchar *db_file_name_const=NULL;
static gchar *db_file_name_var=NULL;

static LMP_ENTRY *lmp_const=NULL;
static LMP0_ENTRY *lmp_var=NULL;

/************************/
/* initialize the MD db */
/***************************/
/* output: 0=ok, !=0=error */
/***************************/
int init_md_db(const char *db_file_name)
{
      db_file_name_const=g_strdup(db_file_name);
      if(db_file_name_const==NULL)
            return 1;

      db_file_name_var=g_strconcat(db_file_name,".var",NULL);
      if(db_file_name_var==NULL)
      {
            g_free(db_file_name_const);
            db_file_name_const=NULL;
            return 2;
      }

      lmp_const=lmp_new(db_file_name_const,sizeof(MDDB_CONST_DATA),PREALLOC_CONST);
      if(lmp_const==NULL)
      {
            g_free(db_file_name_var);
            db_file_name_var=NULL;
            g_free(db_file_name_const);
            db_file_name_const=NULL;
            return 3;
      }

      lmp_var=lmp0_new(db_file_name_var,PREALLOC_VAR);
      if(lmp_var==NULL)
      {
            lmp_close(lmp_const);
            g_free(db_file_name_var);
            db_file_name_var=NULL;
            g_free(db_file_name_const);
            db_file_name_const=NULL;
            return 4;
      }
      return 0;
}

/****************************************************/
/* save the given record in a free space of the LMP */
/****************************************************/
/* output: 0=ok, !=0=error */
/*************************************************/
/* NOTE: after the save, the LMP can be unmapped */
/*       but it remains locked.                  */
/*************************************************/
static int save_a_mdconst_record(LMP_ENTRY *lmp, MDDB_CONST_DATA *mi)
{
      int i;
      MDDB_CONST_DATA *adr;

      for(i=1;i<lmp->nb_records;i++)
      {
            adr=&((MDDB_CONST_DATA*)(lmp->mapped_addr))[i];

            if(!IS_A_BUSY_RECORD(adr))
            {
                  memcpy(adr,mi,sizeof(MDDB_CONST_DATA));
                  return 0;
            }
      }

      return lmp_append_record(lmp,mi);
}

/********************************************************************************/
/* search inside the given LMP a record having the same values as the given one */
/********************************************************************************/
/* g_crc is the file global CRC (MD_BLOC_SIZE bytes) */
/******************************************************************/
/* output: NULL or the address of the record in the mapped memory */
/******************************************************************/
static MDDB_CONST_DATA *find_a_record_with_same_value(LMP_ENTRY *lmp, const guint8 *g_crc, const guint64 file_length)
{
      int i;
      MDDB_CONST_DATA *adr;

      for(i=1;i<lmp->nb_records;i++)
      {
            adr=&((MDDB_CONST_DATA*)(lmp->mapped_addr))[i];

            if(IS_A_BUSY_RECORD(adr))
            {
                  if((adr->filesize==file_length)&&
                     (!memcmp(g_crc,adr->global_crc,MD4_DIGEST_LENGTH)))
                        return adr;
            }
      }

      return NULL;
}

/***********************************************************/
/* save the given record in a free space of the given LMP0 */
/**************************************************************************/
/* input: keep_mapped: if set to TRUE, when lmp0_append_record is called, */
/*                     the file will be remapped. With FALSE, the file    */
/*                     will remains locked but become unmapped            */
/**************************************************************************/
static off_t save_a_record(LMP0_ENTRY *lmp, const guint8 *data, const guint32 data_size, int keep_mapped)
{
      off_t current_pos=0;
      guint8 *base=lmp->mapped_addr;

      while(current_pos<lmp->mapped_size)
      {
            guint32 cur_bloc;
            guint32 ts;

            cur_bloc=GET_UAA_GUINT32(base+current_pos);
            ts=TRUE_SIZE(cur_bloc);       /* just the size of the bloc without any flags */

            if(!HAS_A_BUSY_FLAG(cur_bloc))
            {     /* the bloc is free */
                  if(ts==data_size)
                  {
                        /* magic, the bloc has exactly the wanted size */
                        guint32 sz=TRUE_SIZE(data_size)|RECORD_BUSY;
                        memcpy(base+current_pos,&sz,sizeof(sz));
                        memcpy(base+current_pos+sizeof(sz),data,data_size);
                        return current_pos;
                  }
                  else if(ts>=(data_size+sizeof(guint32)))
                  {
                        off_t new_free_pos;
                        guint32 new_free_size;
                        guint32 sz=TRUE_SIZE(data_size)|RECORD_BUSY;

                        /* if there is more than what we want, we must have data_size+ 1 guint32 */
                        /* because we must set up a new header for the remaining part of empty area */
                        new_free_pos=current_pos+sizeof(guint32)+data_size;
                        new_free_size=ts-data_size-sizeof(guint32);

                        /* the newly allocated bloc */
                        memcpy(base+current_pos,&sz,sizeof(sz));
                        memcpy(base+current_pos+sizeof(sz),data,data_size);

                        /* and the space following the newly allocated bloc */
                        memcpy(base+new_free_pos,&new_free_size,sizeof(new_free_size));

                        return current_pos;
                  }
            }
            current_pos=sizeof(guint32)+ts;
      }

      /* well, there is no free space, we must append the record */
      current_pos=lmp0_append_record(lmp,data_size,data);
      if(keep_mapped==TRUE)
      {
            lmp0_map_locked(lmp);
      }
      return current_pos;     /* error */
}

/*******************************************************************************************/
/* release the given record. If the next record is also free, the 2 records will be merged */
/*******************************************************************************************/
static void release_a_record(LMP0_ENTRY *lmp, off_t offset)
{
      guint32 ssize;
      guint8 *base=lmp->mapped_addr;

      ssize=GET_UAA_GUINT32(base+offset);
      ssize=ssize&(~RECORD_BUSY);

      memcpy(base+offset,&ssize,sizeof(ssize));
}

static void start_garbage_collector(LMP0_ENTRY *lmp)
{
      guint8 *base=lmp->mapped_addr;
      off_t offset;
      guint32 free_bloc_size;
      guint32 bc;
      guint32 sz;

      offset=0;
      free_bloc_size=0;

      while((offset+free_bloc_size)<lmp->mapped_size)
      {
            bc=GET_UAA_GUINT32(base+offset+free_bloc_size);

            if(HAS_A_BUSY_FLAG(bc))
            {
                  if(free_bloc_size!=0)
                  {
                        /* the previous bloc is free */
                        sz=free_bloc_size-sizeof(guint32);  /* remove the size of the header */
                        memcpy(base+offset,&sz,sizeof(sz));
                        offset+=free_bloc_size;
                        free_bloc_size=0;
                  }
                  offset+=sizeof(guint32)+TRUE_SIZE(bc);    /* go to next bloc */
                  free_bloc_size=0;       
            }
            else
            {
                  free_bloc_size+=sizeof(guint32)+TRUE_SIZE(bc);
            }
      }

      if((offset<lmp->mapped_size)&&(free_bloc_size!=0))
      {     
            /* the last bloc is empty */
            free_bloc_size-=sizeof(guint32);    /* remove the size of the header */
            memcpy(base+offset,&free_bloc_size,sizeof(free_bloc_size));
      }
}

/*******************************/
/* add a MD entry to the MD db */
/***************************************************************/
/* g_crc is the file global CRC (MD_BLOC_SIZE bytes)           */
/* l_crc is the set of partial CRC (nb_seg*MD_BLOC_SIZE bytes) */
/***************************************************************/
/* output: 0=already in db, 1=added, -1=error */
/**********************************************/
int md4_add(const guint8 *g_crc, const guint32 file_length, const guint8 *l_crc, const guint32 nb_seg, const char *filename)
{
      MDDB_CONST_DATA *cnst_adr;
      int ret=-1;

      if(lmp_lock_and_map(lmp_const))
            return ret;

      cnst_adr=find_a_record_with_same_value(lmp_const,g_crc,file_length);
      if(cnst_adr==NULL)
      {
            MDDB_CONST_DATA mdconst;

            if(lmp0_lock_and_map(lmp_var))
                  goto abrt;
                  
            /* we must find 2 free area in lmp_var: 1) nb_seg*MD_BLOC_SIZE 2) strlen(filename)+1 */
            mdconst.fname_offset=save_a_record(lmp_var,filename,strlen(filename)+1, TRUE);
            if(mdconst.fname_offset==-1)
            {
                  lmp0_unmap_and_unlock(lmp_var);
                  goto abrt;
            }
            mdconst.l0_crc_offset=save_a_record(lmp_var,l_crc,nb_seg*MD_BLOC_SIZE, TRUE);
            if(mdconst.l0_crc_offset==-1)
            {
                  release_a_record(lmp_var,mdconst.fname_offset);
                  start_garbage_collector(lmp_var);
                  lmp0_unmap_and_unlock(lmp_var);
                  goto abrt;
            }

            mdconst.flags=RECORD_BUSY;
            mdconst.filesize=file_length;
            memcpy(mdconst.global_crc,g_crc,MD4_DIGEST_LENGTH);
            if(save_a_mdconst_record(lmp_const,&mdconst))
            {
                  release_a_record(lmp_var,mdconst.l0_crc_offset);
                  release_a_record(lmp_var,mdconst.fname_offset);
                  start_garbage_collector(lmp_var);
                  lmp0_unmap_and_unlock(lmp_var);
                  goto abrt;
            }
            lmp0_unmap_and_unlock(lmp_var);
            ret=0;
      }
      else
            ret=0;
      abrt:
      lmp_unmap_and_unlock(lmp_const);
      return ret;
}

/***********************************************************/
/* copy the string stored at the given position of the lmp */
/***********************************************************/
static char *dup_string(LMP0_ENTRY *lmp0, off_t offset)
{
      guint32 data_size;
      guint8 *ptr=((guint8*)(lmp0->mapped_addr))+offset;

      data_size=GET_UAA_GUINT32(ptr);
      if(HAS_A_BUSY_FLAG(data_size))
      {
            char *res;
            guint32 tsize;

            tsize=TRUE_SIZE(data_size);

            res=malloc(tsize+1);
            if(res)
            {
                  memcpy(res,ptr+sizeof(guint32),tsize);
                  res[tsize]='\0';
            }
            return res;
      }
      fprintf(stderr,"dup_string: WARNING - offset %lu used by record is empty in lmp0.\n",(unsigned long)offset);
      return NULL;
}

/********************************************************************************/
/* compare a given string with a string stored at the given position of the lmp */
/********************************************************************************/
/* output: TRUE= equal, FALSE= different */
/*****************************************/
static gboolean comp_string(LMP0_ENTRY *lmp0, off_t offset, const char *string)
{
      guint32 data_size;
      guint8 *ptr=((guint8*)(lmp0->mapped_addr))+offset;

      data_size=GET_UAA_GUINT32(ptr);
      if(HAS_A_BUSY_FLAG(data_size))
      {
            guint32 tsize;

            tsize=TRUE_SIZE(data_size);

            if(strlen(string)!=tsize)
                  return FALSE;

            if(!strncmp(string,ptr+sizeof(guint32),tsize))
                  return TRUE;
            return FALSE;
      }
      fprintf(stderr,"comp_string: WARNING - offset %lu used by record is empty in lmp0.\n",(unsigned long)offset);
      return FALSE;
}

/***************************************************************/
/* copy the byte array stored at the given position of the lmp */
/***************************************************************/
static char *dup_array(LMP0_ENTRY *lmp0, off_t offset)
{
      guint32 data_size;
      guint8 *ptr=((guint8*)(lmp0->mapped_addr))+offset;

      data_size=GET_UAA_GUINT32(ptr);
      if(HAS_A_BUSY_FLAG(data_size))
      {
            char *res;
            guint32 tsize;

            tsize=TRUE_SIZE(data_size);

            res=malloc(tsize);
            if(res)
                  memcpy(res,ptr+sizeof(guint32),tsize);
            return res;
      }
      fprintf(stderr,"dup_array: WARNING - offset %lu used by record is empty in lmp0.\n",(unsigned long)offset);
      return NULL;
}

/************************************************************************/
/* search and return info on a MD entry having the provided information */
/************************************************************************/
MD_INFO *get_md4(const guint8 *g_crc, const guint32 file_length)
{
      MDDB_CONST_DATA *cnst_adr;
      MD_INFO *mi=NULL;

      if(lmp_lock_and_map(lmp_const))
            return mi;

      cnst_adr=find_a_record_with_same_value(lmp_const,g_crc,file_length);
      if(cnst_adr!=NULL)
      {
            if(lmp0_lock_and_map(lmp_var)==0)
            {
                  mi=malloc(sizeof(MD_INFO));
                  if(mi!=NULL)
                  {
                        mi->filesize=cnst_adr->filesize;
                        memcpy(mi->global_crc,cnst_adr->global_crc,MD4_DIGEST_LENGTH);
                        mi->nb_seg=(mi->filesize+PARTSIZE-1)/PARTSIZE;
                        mi->filename=dup_string(lmp_var,cnst_adr->fname_offset);
                        mi->partial_crc=dup_array(lmp_var,cnst_adr->l0_crc_offset);
                  }
                  lmp0_unmap_and_unlock(lmp_var);
            }
      }
      lmp_unmap_and_unlock(lmp_const);
      return mi;
}

/**************************************************************/
/* free a MD_INFO structure returned by the previous function */
/**************************************************************/
void free_md_info(MD_INFO *nfo)
{
      if(nfo!=NULL)
      {
            if(nfo->filename!=NULL)
                  free(nfo->filename);
            if(nfo->partial_crc!=NULL)
                  free(nfo->partial_crc);
            free(nfo);
      }
}

/*************************************************************/
/* search if a filename:file_length is already inside the db */
/*************************************************************/
gboolean md4_is_inside(const guint32 file_length, const char *filename)
{
      gboolean ret=FALSE;
      MDDB_CONST_DATA *cnst_adr;
      int i;

      if(lmp_lock_and_map(lmp_const))
            return ret;

      if(lmp0_lock_and_map(lmp_var)==0)
      {
            for(i=1;i<lmp_const->nb_records;i++)
            {
                  cnst_adr=&((MDDB_CONST_DATA*)(lmp_const->mapped_addr))[i];
      
                  if(IS_A_BUSY_RECORD(cnst_adr))
                  {
                        if((cnst_adr->filesize==file_length)&&
                           (comp_string(lmp_var,cnst_adr->fname_offset,filename)==TRUE))
                        {
                              ret=TRUE;
                              break;
                        }
                  }
            }
            lmp0_unmap_and_unlock(lmp_var);
      }

      lmp_unmap_and_unlock(lmp_const);

      return ret;
}


Generated by  Doxygen 1.6.0   Back to index