Logo Search packages:      
Sourcecode: ed2k-hash version File versions  Download package

processfile.c

/***************************************************************************
                          processfile.c  -  description
                             -------------------
    begin                : Tue Sep 3 2002
    copyright            : (C) 2002 by Tim-Philipp Müller
    email                : t.i.m@orange.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "global.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#undef ED2K_HASH_USE_MMAP

/* for nmap on unix systems             */
/* TODO: add other *nix OS defines here */
#if (defined(__linux__) || defined(__MAC_OS_X__) || defined(__FreeBSD__))
#  include <unistd.h>
#  include <sys/mman.h>
#  include <sys/types.h>
#  include <sys/stat.h>
#  include <fcntl.h>
#  ifdef _POSIX_MAPPED_FILES
#    define ED2K_HASH_USE_MMAP      1
#  endif
#endif

#include "md4.h"
#include "options.h"
#include "processfile.h"
#include "ui.h"

/* functions */
static unsigned int                 get_file_size (const char *fn);

#ifndef ED2K_HASH_USE_MMAP
static int                          process_one_block (fileinfo *fi, unsigned int b);
#endif


/* process_file
 *
 * gets the file size of the file and hashes it.
 *
 * If info is set, it will fill info
 *   with a fileinfo structure (which is undefined on error)
 *
 * The function will regularly call ui_update(), and will
 *   stop hashing if ui_update() returns a negative value.
 *
 * returns 0 on error, otherwise 1
 *
 */

#ifdef ED2K_HASH_USE_MMAP

/************************** version with mmap() ********************************/
/* version using mmap() by Thomas Lussnig <thomas.lussnig@bewegungsmelder.de> */

int
process_file (const char *fn, fileinfo *info)
{
      const char        hexdigits[16] = "0123456789abcdef";
      unsigned int      b, j;
      fileinfo          fi;
      int                     fd;

      if (!fn)
            return 0;

      memset (&fi,0x00,sizeof(fi));

      /* get filesize */
      fi.size = get_file_size (fn);
      if (fi.size == (unsigned int)-1)    /* error getting filesize? */
      {
            ui_printerr ("error getting filesize ('%s')?\n", fn);
            return 0;
      }

      if (fi.size == 0)                         /* a filesize of 0 bytes? */
      {
            ui_printerr ("filesize of 0 bytes ('%s')?\n", fn);
            return 0;
      }

      fi.filepath = strdup(fn);

      if (!strrchr(fn,SLASH_CHAR))        /* get the basename without path components */
      {
            fi.basename = strdup(fn);
      } else fi.basename = strdup(strrchr(fn,SLASH_CHAR)+1);

      fi.blocks = fi.size/BLOCKSIZE;
      if (fi.size % BLOCKSIZE > 0)
            fi.blocks++;

      fi.ed2k_hash = (unsigned char*) malloc(16);
      fi.ed2k_hash_str = (char*) malloc(33);
      fi.parthashes = (unsigned char*) malloc(fi.blocks*16);      /* 16 bytes for each block's hash */

      if ((!fi.parthashes) || (!fi.ed2k_hash) || (!fi.ed2k_hash_str) || (!fi.filepath))
            return 0;

      if ((fd = open (fi.filepath, O_RDONLY)) < 0)
      {
            ui_printerr ("in %s - fopen(%s) failed: %s\n\n", __FUNCTION__, fi.filepath, strerror(errno));
            return 0;
      }

      for (b=0; b < fi.blocks; b++)
      {
            MD4_CTX            context;
            int                      len, start;
            void              *map;

            len =  BLOCKSIZE;
            if (b == fi.blocks - 1)
                  len = fi.size % BLOCKSIZE;

            if (len==0) /* this case shouldn't happen */
                  return 0;

            start = b*BLOCKSIZE;
            map = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, b*BLOCKSIZE);
            if (map == NULL)
            {
                  ui_printerr ("in %s - mmap() failed: %s\n", __FUNCTION__, strerror(errno));
                  return 0;
            }
            MD4_Init   (&context);
            MD4_Update (&context, map, len);
            MD4_Final  (fi.parthashes+(b*16), &context);
            munmap (map, len);
            if ((option_debug)||(option_verbose))
            {
                  int k;
                  ui_print ("partial hash block %3u: ", b);
                  for (k=0; k<16; k++)
                        ui_print ("%02hx", (fi.parthashes+(b*16))[k]);
                  ui_print ("\n");
            }
      } /* end of: for (b=0; b < fi.blocks; b++) */

      close(fd);

      /* if only one block: partial hash == final hash */
      if (fi.blocks>1)
      {
            MD4_CTX     context;
            MD4_Init (&context);
            MD4_Update (&context, fi.parthashes, 16*b);
            MD4_Final (fi.ed2k_hash, &context);
      } else memcpy (fi.ed2k_hash, fi.parthashes, 16);


      /* hash to hash string: */
      memset (fi.ed2k_hash_str, 0x00, 33*sizeof(char));
      for (j=0; j<16; j++)
      {
            fi.ed2k_hash_str[(j<<1)  ] = hexdigits[(((fi.ed2k_hash[j]) & 0xf0) >> 4)];
            fi.ed2k_hash_str[(j<<1)+1] = hexdigits[(((fi.ed2k_hash[j]) & 0x0f)     )];
      }

      if (info)
            memcpy (info, &fi, sizeof(fi));

      return 1;
}



#else



/************************** version without mmap() *****************************/

int
process_file (const char *fn, fileinfo *info)
{
      const char        hexdigits[16] = "0123456789abcdef";
      unsigned int      b, j;
      fileinfo          fi;

      if (!fn)
            return 0;

      memset (&fi,0x00,sizeof(fi));

      /* get filesize */
      fi.size = get_file_size (fn);
      if (fi.size == (unsigned int)-1)    /* error getting filesize? */
      {
            ui_printerr ("error getting filesize ('%s')?\n", fn);
            return 0;
      }

      if (fi.size == 0)                         /* a filesize of 0 bytes? */
      {
            ui_printerr ("filesize of 0 bytes ('%s')?\n", fn);
            return 0;
      }

      fi.filepath = strdup(fn);

      if (!strrchr(fn,SLASH_CHAR))        /* get the basename without path components */
      {
            fi.basename = strdup(fn);
      } else fi.basename = strdup(strrchr(fn,SLASH_CHAR)+1);

      fi.blocks = fi.size/BLOCKSIZE;
      if (fi.size % BLOCKSIZE > 0)
            fi.blocks++;

      fi.ed2k_hash = (unsigned char*) malloc(16);
      fi.ed2k_hash_str = (char*) malloc(33);
      fi.parthashes = (unsigned char*) malloc(fi.blocks*16);      /* 16 bytes for each block's hash */

      if ((!fi.parthashes) || (!fi.ed2k_hash) || (!fi.ed2k_hash_str))
            return 0;

      for (b=0; b < fi.blocks; b++)
      {
            if (!process_one_block (&fi, b))
                  return 0;
      }

      /* if only one block: partial hash == final hash */
      if (fi.blocks>1)
      {
            MD4_CTX     context;
            MD4_Init (&context);
            MD4_Update (&context, fi.parthashes, 16*b);
            MD4_Final (fi.ed2k_hash, &context);
      } else memcpy (fi.ed2k_hash, fi.parthashes, 16);


      /* hash to hash string: */
      memset (fi.ed2k_hash_str, 0x00, 33*sizeof(char));
      for (j=0; j<16; j++)
      {
            fi.ed2k_hash_str[(j<<1)  ] = hexdigits[(((fi.ed2k_hash[j]) & 0xf0) >> 4)];
            fi.ed2k_hash_str[(j<<1)+1] = hexdigits[(((fi.ed2k_hash[j]) & 0x0f)     )];
      }

      if (info)
            memcpy (info, &fi, sizeof(fi));

      return 1;
}



/* process_one_block
 *
 * hashes one block of 9500kB of the file and
 * calls ui_update() in between.
 *
 * returns 0 on error, otherwise 1
 *
 */

static int
process_one_block (fileinfo *fi, unsigned int b)
{
      MD4_CTX                  context;
      FILE              *f;
      unsigned int       blocksize = BLOCKSIZE;
      unsigned int       left;
      unsigned char      hash[16];

      if ((!fi) || (!fi->parthashes) || (!fi->filepath))
            return 0;

      f = fopen (fi->filepath, "rb");
      if (!f)
      {
            ui_printerr ("in %s - fopen(%s) failed: %s\n\n", __FUNCTION__, fi->filepath, strerror(errno));
            return 0;
      }

      if (fseek(f, b*BLOCKSIZE, SEEK_SET)<0)
      {
            ui_printerr ("in %s - fseek() failed: %s\n", __FUNCTION__, strerror(errno));
            return 0;
      }

      if (b == fi->blocks - 1)
            blocksize = fi->size % BLOCKSIZE;

      if (blocksize==0) /* this case shouldn't happen */
            return 0;

      left = blocksize;
      MD4_Init (&context);
      while (left>0)
      {
            unsigned char     buf[4*1024];
            int               ret;
            size_t                  readnow = (left>=sizeof(buf)) ? sizeof(buf) : left;

            ret = fread(buf, readnow, 1, f);
            if (ret!=1)
            {
                  ui_printerr ("in %s - fread(%s) failed: %s\n", __FUNCTION__, fi->filepath, strerror(errno));
                  fclose(f);
                  return 0;
            }

            MD4_Update (&context, (void*)buf, readnow);

            left -= readnow;

            if (!ui_update (fi->filepath, fi->size, fi->size-left))
                  return 0;

      }

      MD4_Final (hash, &context);
      memcpy (fi->parthashes+(b*16), hash, 16);

      if ((option_debug)||(option_verbose))
      {
            int k;
            ui_print ("partial hash block %3u: ", b);
            for (k=0; k<16; k++)
                  ui_print ("%02hx", hash[k]);
            ui_print ("\n");
      }

      fclose(f);
      return 1;
}


#endif /* ifdef ED2K_HASH_USE_MMAP ... else ...*/

/* process_file_free_info_structure_content
 *
 * frees the fields of the fileinfo structure,
 * but NOT the structure itself,
 * and NOT the info->user_data field
 *  (which must be freed by the UI if it was used)
 *
 */

void
process_file_free_info_structure_content (fileinfo *info)
{
      if (!info)
            return;

      if (info->filepath)
            free(info->filepath);
      if (info->basename)
            free(info->basename);
      if (info->parthashes)
            free(info->parthashes);
      if (info->ed2k_hash)
            free(info->ed2k_hash);
      if (info->ed2k_hash_str)
            free(info->ed2k_hash_str);
}



/* get_file_size
 *
 * unelegant but portable way to get the size of a file
 *
 * returns (unsigned int)-1 on error, otherwise filesize
 *
 */

static unsigned int
get_file_size (const char *fn)
{
      FILE              *file;
      unsigned long      fsize;

      if (!fn)
            return (unsigned int)-1;

      file = fopen(fn, "rb");
      if (!file)
      {
            ui_printerr ("in fopen(%s) - %s\n\n", fn, strerror(errno));
            return (unsigned int)-1;
      }

      if (fseek(file, 0, SEEK_END)<0)           /* go to very last byte of file */
            return (unsigned int)-1;

      fsize = ftell(file);                      /* and tell us which no. that is */
      if (fsize == (unsigned long) -1)
            return (unsigned int)-1;

      fclose(file);

      if (fsize > (unsigned long)((unsigned int)-1))
      {
            ui_printerr ("the file %s is too big. The donkey can only deal with files smaller than 4GB!\n\n", fn);
            return ((unsigned int)-1);
      }

      return (unsigned int)fsize;
}







Generated by  Doxygen 1.6.0   Back to index