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

lmp.c

/* DCTC - a Direct Connect text clone for Linux
 * Copyright (C) 2001 Eric Prevoteau
 *
 * lmp.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: lmp.c,v 1.3 2003/12/28 08:12:38 uid68112 Exp $
*/

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/mman.h>
#include <glib.h>

#define min(a,b)        (((a)<(b))?(a):(b))

#include "lmp.h"

/******************************************************/
/* create a new LMP entry (and the file if not exist) */
/********************************************************************/
/* input: prealloc_records= -1, no prealloc else, try to immediatly */
/*        allocate space on disc (it is not in bytes but in record) */
/*        The lack of free disc space is an error                   */
/* output: a newly allocated LMP entry or NULL                      */
/********************************************************************/
LMP_ENTRY *lmp_new(const char *filename, int record_size, long int prealloc_records)
{
      LMP_ENTRY nw;
      LMP_ENTRY *real_nw=NULL;

      nw.record_size=record_size;

      nw.fd=open(filename,O_CREAT|O_RDWR,0666);
      if(nw.fd==-1)
      {
            perror("lmp_new - open fails");
            return real_nw;
      }

      if(flock(nw.fd,LOCK_EX|LOCK_NB)!=-1)
      {
            /* the file is locked. 2 cases: */
            /* the file is empty and we must create its header */
            /* the file is not empty and is already ready to be used */
            struct stat st;

            if(fstat(nw.fd,&st)==-1)
            {
                  perror("lmp_new - fstat fails");
                  close(nw.fd);
                  return real_nw;
            }

            if(st.st_size==0)
            {
                  /* we have created the file, we must build its header */
                  int i;
                  
                  for(i=0;i<record_size;i++)
                  {
                        if(write(nw.fd,"",1)!=1)
                        {
                              perror("lmp_new - write fails");
                              ftruncate(nw.fd,0);
                              close(nw.fd);
                              return real_nw;
                        }
                  }

                  if(prealloc_records>0)
                  {
                        char buf[8192];
                        int reserve=prealloc_records*record_size;

                        memset(buf,0,sizeof(buf));
                        while(reserve>0)
                        {
                              i=min(sizeof(buf),reserve);
                              if(write(nw.fd,buf,i)!=i)
                              {
                                    perror("lmp_new - write fails");
                                    ftruncate(nw.fd,0);
                                    close(nw.fd);
                                    return real_nw;
                              }
                              reserve-=i;
                        }
                  }
            }
            else
            {
                  /* the file is already ready to be used */
            }

            flock(nw.fd,LOCK_UN);         /* unlock the file */
      }
      else
      {
            /* unable to lock the file. Either someone already using it or initializing it */
            /* in both case, the file is ready */
      }

      real_nw=malloc(sizeof(LMP_ENTRY));
      if(real_nw==NULL)
      {
            perror("lmp_new - malloc fails");
            close(nw.fd);
      }
      else
      {
            nw.is_locked=0;
            nw.mapped_addr=NULL;
            nw.mapped_size=0;
            nw.nb_records=0;
            memcpy(real_nw,&nw,sizeof(LMP_ENTRY));
      }
      return real_nw;
}


/***********************************/
/* lock and map a file into memory */
/***********************************/
/* output: 0=ok, 1=error */
/*************************/
int lmp_lock_and_map(LMP_ENTRY *lmp)
{
   struct stat st;

      /* already locked or mapped ? */
      if((lmp->is_locked!=0)||(lmp->mapped_addr!=NULL))
            return 1;

   flock(lmp->fd,LOCK_EX);
      lmp->is_locked=1;

   if(fstat(lmp->fd,&st)==-1)
   {
      lmp_unmap_and_unlock(lmp);
      return 1;
   }

   lmp->mapped_size=st.st_size;
   lmp->mapped_addr=mmap(NULL,lmp->mapped_size,PROT_READ|PROT_WRITE,MAP_SHARED, lmp->fd, 0);
   if(lmp->mapped_addr==MAP_FAILED)
   {
      lmp_unmap_and_unlock(lmp);
      return 1;
   }

   lmp->nb_records=lmp->mapped_size/lmp->record_size;
   return 0;
}


/*********************************************/
/* unmap file from memory, it remains locked */
/*********************************************/
void inline lmp_unmap(LMP_ENTRY *lmp)
{
      if(lmp->mapped_addr!=NULL)
      {
      munmap(lmp->mapped_addr,lmp->mapped_size);
      lmp->mapped_addr=NULL;
            lmp->mapped_size=0;
            lmp->nb_records=0;
      }
}


/**************************************************************/
/* unlock a file. If file is not yet unmapped, it is unmapped */
/**************************************************************/
void lmp_unmap_and_unlock(LMP_ENTRY *lmp)
{
      lmp_unmap(lmp);

      if(lmp->is_locked)
      {
            flock(lmp->fd,LOCK_UN);
            lmp->is_locked=0;
      }
}


/***************************************************************/
/* close file and destroy LMP entry (the file remains present) */
/***************************************************************/
void lmp_close(LMP_ENTRY *lmp)
{
      lmp_unmap_and_unlock(lmp);

      close(lmp->fd);
      free(lmp);
}

/********************************************************************/
/* append the given record to the given LMP. The LMP must be locked */
/* After the call, the LMP remains locked but becomes unmapped      */
/********************************************************************/
/* output: 0=ok, 1=error */
/*************************/
int lmp_append_record(LMP_ENTRY *lmp, void *value)
{
      struct stat st;
      off_t pos;

      if(lmp->is_locked==0)
            return 1;

      lmp_unmap(lmp);

      if(fstat(lmp->fd,&st)==-1)
      {
            perror("lmp_append_record - fstat fails");
            return 1;
      }

      pos=(st.st_size/lmp->record_size)*lmp->record_size;   /* round the position to the last full record */
      if(lseek(lmp->fd,pos,SEEK_SET)!=pos)
      {
            perror("lmp_append_record - lseek fails");
            return 1;
      }

      if(write(lmp->fd,value,lmp->record_size)!=lmp->record_size)
      {
            perror("lmp_append_record - write fails");
            return 1;
      }

      return 0;
}

/* -------------------------------------------------------- */
/* ------------------------ LMP0 -------------------------- */
/* -------------------------------------------------------- */
/*******************************************************/
/* create a new LMP0 entry (and the file if not exist) */
/********************************************************************/
/* input: prealloc_records= -1, no prealloc else, try to immediatly */
/*        allocate space on disc (it is not in bytes but in record) */
/*        The lack of free disc space is an error                   */
/* output: a newly allocated LMP entry or NULL                      */
/********************************************************************/
LMP0_ENTRY *lmp0_new(const char *filename, long int prealloc_records)
{
      LMP0_ENTRY nw;
      LMP0_ENTRY *real_nw=NULL;

      if(prealloc_records==-1)
      {
            prealloc_records=sizeof(guint32);
      }
      else if(prealloc_records<sizeof(guint32))
      {
            fprintf(stderr,"WARNING: lmp0_new: min prealloc_size == %d\n",sizeof(guint32));
            prealloc_records=sizeof(guint32);
      }

      nw.fd=open(filename,O_CREAT|O_RDWR,0666);
      if(nw.fd==-1)
      {
            perror("lmp0_new - open fails");
            return real_nw;
      }

      if(flock(nw.fd,LOCK_EX|LOCK_NB)!=-1)
      {
            /* the file is locked. 2 cases: */
            /* the file is empty and we must create its header */
            /* the file is not empty and is already ready to be used */
            struct stat st;

            if(fstat(nw.fd,&st)==-1)
            {
                  perror("lmp0_new - fstat fails");
                  close(nw.fd);
                  return real_nw;
            }

            if(st.st_size==0)
            {
                  /* we have created the file, we must build its header */
                  guint32 bkheader=prealloc_records-sizeof(guint32);
                  unsigned long reserve;
                  long int i;
                  char buf[2048];

                  if(write(nw.fd,&bkheader,sizeof(bkheader))!=sizeof(bkheader))
                  {
                        perror("lmp0_new - write fails");
                        ftruncate(nw.fd,0);
                        close(nw.fd);
                        return real_nw;
                  }

                  reserve=bkheader;
                  memset(buf,0,sizeof(buf));
                  while(reserve>0)
                  {
                        i=min(sizeof(buf),reserve);
                        if(write(nw.fd,buf,i)!=i)
                        {
                              perror("lmp0_new - write fails");
                              ftruncate(nw.fd,0);
                              close(nw.fd);
                              return real_nw;
                        }
                        reserve-=i;
                  }
            }
            else
            {
                  /* the file is already ready to be used */
            }

            flock(nw.fd,LOCK_UN);         /* unlock the file */
      }
      else
      {
            /* unable to lock the file. Either someone already using it or initializing it */
            /* in both case, the file is ready */
      }

      real_nw=malloc(sizeof(LMP0_ENTRY));
      if(real_nw==NULL)
      {
            perror("lmp0_new - malloc fails");
            close(nw.fd);
      }
      else
      {
            nw.is_locked=0;
            nw.mapped_addr=NULL;
            nw.mapped_size=0;
            memcpy(real_nw,&nw,sizeof(LMP0_ENTRY));
      }
      return real_nw;
}


/***********************************/
/* lock and map a file into memory */
/***********************************/
/* output: 0=ok, 1=error */
/*************************/
int lmp0_lock_and_map(LMP0_ENTRY *lmp)
{
   struct stat st;

      /* already locked or mapped ? */
      if((lmp->is_locked!=0)||(lmp->mapped_addr!=NULL))
            return 1;

   flock(lmp->fd,LOCK_EX);
      lmp->is_locked=1;

   if(fstat(lmp->fd,&st)==-1)
   {
      lmp0_unmap_and_unlock(lmp);
      return 1;
   }

   lmp->mapped_size=st.st_size;
   lmp->mapped_addr=mmap(NULL,lmp->mapped_size,PROT_READ|PROT_WRITE,MAP_SHARED, lmp->fd, 0);
   if(lmp->mapped_addr==MAP_FAILED)
   {
      lmp0_unmap_and_unlock(lmp);
      return 1;
   }
   return 0;
}


/*********************************************/
/* unmap file from memory, it remains locked */
/*********************************************/
void inline lmp0_unmap(LMP0_ENTRY *lmp)
{
      if(lmp->mapped_addr!=NULL)
      {
      munmap(lmp->mapped_addr,lmp->mapped_size);
      lmp->mapped_addr=NULL;
            lmp->mapped_size=0;
      }
}

/***************************************************/
/* map a file into memory, the file must be locked */
/***************************************************/
/* output: 0=ok, 1=error */
/*************************/
int lmp0_map_locked(LMP0_ENTRY *lmp)
{
   struct stat st;

      if(lmp->mapped_addr!=NULL)
            return 0;   /* already mapped */
      if(lmp->is_locked==0)
            return 1;   /* not locked */

   if(fstat(lmp->fd,&st)==-1)
      return 1;

   lmp->mapped_size=st.st_size;
   lmp->mapped_addr=mmap(NULL,lmp->mapped_size,PROT_READ|PROT_WRITE,MAP_SHARED, lmp->fd, 0);
   if(lmp->mapped_addr==MAP_FAILED)
   {
      lmp->mapped_addr=NULL;
      return 1;
   }
   return 0;
}

/**************************************************************/
/* unlock a file. If file is not yet unmapped, it is unmapped */
/**************************************************************/
void lmp0_unmap_and_unlock(LMP0_ENTRY *lmp)
{
      lmp0_unmap(lmp);

      if(lmp->is_locked)
      {
            flock(lmp->fd,LOCK_UN);
            lmp->is_locked=0;
      }
}


/***************************************************************/
/* close file and destroy LMP entry (the file remains present) */
/***************************************************************/
void lmp0_close(LMP0_ENTRY *lmp)
{
      lmp0_unmap_and_unlock(lmp);

      close(lmp->fd);
      free(lmp);
}

#define BUSY_FLAG 0x80000000

/********************************************************************/
/* append the given record to the given LMP. The LMP must be locked */
/* After the call, the LMP remains locked but becomes unmapped      */
/********************************************************************/
/* output: -1=error, else position of the row in the LMP file */
/**************************************************************/
off_t lmp0_append_record(LMP0_ENTRY *lmp, guint32 data_len, const guint8 *data)
{
      struct stat st;
      off_t pos;
      guint32 bkheader;

      if(lmp->is_locked==0)
            return -1;

      lmp0_unmap(lmp);

      if(fstat(lmp->fd,&st)==-1)
      {
            perror("lmp0_append_record - fstat fails");
            return -1;
      }

      pos=st.st_size;
      if(lseek(lmp->fd,pos,SEEK_SET)!=pos)
      {
            perror("lmp0_append_record - lseek fails");
            return -1;
      }

      bkheader=BUSY_FLAG|data_len;

      if(write(lmp->fd,&bkheader,sizeof(guint32))!=sizeof(guint32))
      {
            perror("lmp0_append_record - write fails");
            ftruncate(lmp->fd,pos);       /* restore file to its previous size */
            return -1;
      }

      if(write(lmp->fd,data,data_len)!=data_len)
      {
            perror("lmp_append_record - write fails");
            ftruncate(lmp->fd,pos);       /* restore file to its previous size */
            return -1;
      }

      return pos;
}



Generated by  Doxygen 1.6.0   Back to index