/*
File: hdcache.c
Copyright (C) 2005-2007 Christophe GRENIER <grenier@cgsecurity.org>
This software 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 the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include "types.h"
#include "common.h"
#include "hdcache.h"
#include "fnctdsk.h"
#include "log.h"
#define CACHE_BUFFER_NBR 128
#define CACHE_SIZE_MAX 64*512
//#define DEBUG_CACHE 1
struct cache_buffer_struct
{
unsigned char *buffer;
unsigned int cache_size;
uint64_t cache_offset;
int cache_status;
};
struct cache_struct
{
disk_t *disk_car;
struct cache_buffer_struct cache[CACHE_BUFFER_NBR];
uint64_t nbr_fnct_sect;
uint64_t nbr_read_sect;
unsigned int nbr_fnct_call;
unsigned int nbr_read_call;
unsigned int cache_buffer_nbr;
unsigned int cache_size_min;
};
static int cache_read(disk_t *disk_car,const unsigned int count, void *nom_buffer, const uint64_t offset);
static int cache_write(disk_t *disk_car,const unsigned int count, const void *nom_buffer, const uint64_t offset);
static int cache_clean(disk_t *clean);
static const char *cache_description(disk_t *disk_car);
static const char *cache_description_short(disk_t *disk_car);
static int cache_read_aux(disk_t *disk_car,const unsigned int count, void *nom_buffer, const uint64_t offset, const unsigned int can_read_more);
static int cache_read(disk_t *disk_car,const unsigned int count, void *nom_buffer, const uint64_t offset)
{
return cache_read_aux(disk_car, count, nom_buffer, offset, 1);
}
static int cache_read_aux(disk_t *disk_car,const unsigned int count, void *nom_buffer, const uint64_t offset, const unsigned int can_read_more)
{
struct cache_struct *data=disk_car->data;
#ifdef DEBUG_CACHE
log_trace("cache_read(count=%u,buffer,offset=%llu)\n", count,(long long unsigned)offset);
#endif
data->nbr_fnct_call++;
{
unsigned int i;
int res=0;
/* Data is probably in the last two buffers */
unsigned int cache_buffer_nbr=(data->cache_buffer_nbr+CACHE_BUFFER_NBR-1)%CACHE_BUFFER_NBR;
for(i=0;i<CACHE_BUFFER_NBR;i++,cache_buffer_nbr=(cache_buffer_nbr+1)%CACHE_BUFFER_NBR)
{
struct cache_buffer_struct *cache=&data->cache[cache_buffer_nbr];
if(cache->buffer!=NULL && cache->cache_size>0 &&
cache->cache_offset <= offset &&
offset < cache->cache_offset +cache->cache_size)
{
unsigned data_available=cache->cache_offset +cache->cache_size-offset;
/*
if(cache_buffer_nbr==data->cache_buffer_nbr)
log_trace("hit\n");
else
log_trace("bid\n");
*/
#ifdef DEBUG_CACHE
log_trace("use cache %u count=%u, offset=%llu\n",i,
cache->cache_size, cache->cache_offset);
#endif
res=cache->cache_status;
if(count<=data_available)
{
data->nbr_fnct_sect+=count;
memcpy(nom_buffer, cache->buffer+offset-cache->cache_offset, count);
return res;
}
else
{
int newres;
data->nbr_fnct_sect+=data_available;
memcpy(nom_buffer, cache->buffer+offset-cache->cache_offset, data_available);
newres=cache_read_aux(disk_car, count-data_available,
(unsigned char*)nom_buffer+data_available, offset+data_available, can_read_more);
if(res>=0)
res=newres;
return res;
}
}
}
}
if(count>CACHE_SIZE_MAX)
{
unsigned int i;
int res=0;
for(i=0;i*CACHE_SIZE_MAX<count;i++)
{
int newres;
newres=cache_read_aux(disk_car, (count>(i+1)*CACHE_SIZE_MAX?CACHE_SIZE_MAX:count-i*CACHE_SIZE_MAX),
(unsigned char*)nom_buffer+i*CACHE_SIZE_MAX, offset+i*CACHE_SIZE_MAX, can_read_more);
if(res>=0)
res=newres;
}
return res;
}
{
struct cache_buffer_struct *cache;
int res;
unsigned int count_new=(can_read_more!=0 && count<data->cache_size_min && (offset+data->cache_size_min<data->disk_car->disk_real_size)?data->cache_size_min:count);
data->nbr_fnct_sect+=count;
data->nbr_read_call++;
data->nbr_read_sect+=count_new;
#ifdef DEBUG_CACHE
log_trace("read(count=%u,buffer,offset=%llu)\n", count_new,(long long unsigned)offset);
#endif
data->cache_buffer_nbr=(data->cache_buffer_nbr+1)%CACHE_BUFFER_NBR;
cache=&data->cache[data->cache_buffer_nbr];
if(cache->buffer==NULL)
cache->buffer=MALLOC(CACHE_SIZE_MAX);
res=data->disk_car->read(data->disk_car, count_new, cache->buffer, offset);
cache->cache_size=count_new;
cache->cache_offset=offset;
cache->cache_status=res;
if(res<0)
{ /* read failure */
unsigned int i;
if(count<=disk_car->sector_size || disk_car->sector_size<=0)
{
memset(cache->buffer, 0, cache->cache_size);
memcpy(nom_buffer, cache->buffer, count);
return res;
}
/* split the read sector by sector */
cache->cache_size=0;
res=-1;
for(i=0;i*disk_car->sector_size<count;i++)
{
int newres;
newres=cache_read_aux(disk_car, (count>(i+1)*disk_car->sector_size?disk_car->sector_size:count - i*disk_car->sector_size), (unsigned char*)nom_buffer+i*disk_car->sector_size, offset+i*disk_car->sector_size,0);
/* If one read succeed, considered that's ok, we are doing data recovery */
if(newres>=0)
res=0;
}
return res;
}
memcpy(nom_buffer, cache->buffer, count);
#ifdef DEBUG_CACHE
log_trace("cache_read offset=%llu size=%lu, update cache %u, res=%d\n",
(long long unsigned)cache->cache_offset,
(long unsigned)cache->cache_size,
data->cache_buffer_nbr-1, res);
#endif
return res;
}
}
static int cache_write(disk_t *disk_car,const unsigned int count, const void *nom_buffer, const uint64_t offset)
{
struct cache_struct *data=disk_car->data;
unsigned int i;
for(i=0;i<CACHE_BUFFER_NBR;i++)
{
struct cache_buffer_struct *cache=&data->cache[i];
if(!(cache->cache_offset+cache->cache_size-1 < offset || offset+count-1 < cache->cache_offset))
{
/* Discard the cache */
cache->cache_size=0;
}
}
disk_car->write_used=1;
return data->disk_car->write(data->disk_car,count,nom_buffer,offset);
}
static int cache_clean(disk_t *disk_car)
{
if(disk_car->data)
{
struct cache_struct *data=disk_car->data;
unsigned int i;
#ifdef DEBUG_CACHE
log_trace("%s\ncache_read total_call=%u, total_count=%llu\n read total_call=%u, total_count=%llu\n",
data->disk_car->description(data->disk_car),
data->nbr_fnct_call, (long long unsigned)data->nbr_fnct_sect,
data->nbr_read_call, (long long unsigned)data->nbr_read_sect);
#endif
data->disk_car->clean(data->disk_car);
for(i=0;i<CACHE_BUFFER_NBR;i++)
{
struct cache_buffer_struct *cache=&data->cache[i];
if(cache->buffer!=NULL)
free(cache->buffer);
}
free(data->disk_car);
free(disk_car->data);
disk_car->data=NULL;
}
return 0;
}
disk_t *new_diskcache(disk_t *disk_car, const unsigned int testdisk_mode)
{
unsigned int i;
struct cache_struct*data=MALLOC(sizeof(*data));
disk_t *new_disk_car=MALLOC(sizeof(*new_disk_car));
memcpy(new_disk_car,disk_car,sizeof(*new_disk_car));
data->disk_car=disk_car;
data->nbr_fnct_sect=0;
data->nbr_read_sect=0;
data->nbr_fnct_call=0;
data->nbr_read_call=0;
data->cache_buffer_nbr=0;
if(testdisk_mode&TESTDISK_O_READAHEAD_8K)
data->cache_size_min=16*512;
else if(testdisk_mode&TESTDISK_O_READAHEAD_32K)
data->cache_size_min=64*512;
else
data->cache_size_min=0;
dup_CHS(&new_disk_car->CHS,&disk_car->CHS);
new_disk_car->disk_size=disk_car->disk_size;
new_disk_car->disk_real_size=disk_car->disk_real_size;
new_disk_car->halt_on_errors=0;
new_disk_car->write_used=0;
new_disk_car->data=data;
new_disk_car->read=cache_read;
new_disk_car->write=cache_write;
new_disk_car->clean=cache_clean;
new_disk_car->description=cache_description;
new_disk_car->description_short=cache_description_short;
for(i=0;i<CACHE_BUFFER_NBR;i++)
data->cache[i].buffer=NULL;
return new_disk_car;
}
static const char *cache_description(disk_t *disk_car)
{
struct cache_struct *data=disk_car->data;
dup_CHS(&data->disk_car->CHS,&disk_car->CHS);
data->disk_car->disk_size=disk_car->disk_size;
return data->disk_car->description(data->disk_car);
}
static const char *cache_description_short(disk_t *disk_car)
{
struct cache_struct *data=disk_car->data;
dup_CHS(&data->disk_car->CHS,&disk_car->CHS);
data->disk_car->disk_size=disk_car->disk_size;
return data->disk_car->description_short(data->disk_car);
}
syntax highlighted by Code2HTML, v. 0.9.1