testdisk/src/io_redir.c
2009-02-03 09:29:29 +01:00

232 lines
7 KiB
C

/*
File: io_redir.c
Copyright (C) 1998-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
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <errno.h>
#include "types.h"
#include "common.h"
#include "io_redir.h"
#include "log.h"
//#define DEBUG_IO_REDIR 1
typedef struct struclist_redir_t list_redir_t;
struct struclist_redir_t
{
uint64_t org_offset;
uint64_t new_offset;
unsigned int size;
const void *mem;
list_redir_t *prev;
list_redir_t *next;
};
struct info_io_redir
{
disk_t *disk_car;
list_redir_t *list_redir;
};
static int io_redir_pread(disk_t *disk_car, void *buffer, const unsigned int count, const uint64_t offset);
static int io_redir_clean(disk_t *clean);
int io_redir_add_redir(disk_t *disk_car, const uint64_t org_offset, const unsigned int size, const uint64_t new_offset, const void *mem)
{
if(disk_car->pread!=io_redir_pread)
{
struct info_io_redir*data=(struct info_io_redir*)MALLOC(sizeof(*data));
disk_t *old_disk_car=(disk_t *)MALLOC(sizeof(*old_disk_car));
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_add_redir: install functions org_offset=%llu, size=%u, new_offset=%llu, mem=%p\n",
(long long unsigned)org_offset,
size,
(long long unsigned)new_offset,
mem);
#endif
memcpy(old_disk_car,disk_car,sizeof(*old_disk_car));
data->disk_car=old_disk_car;
data->list_redir=NULL;
disk_car->write_used=0;
disk_car->data=data;
disk_car->description=old_disk_car->description;
disk_car->pwrite=old_disk_car->pwrite;
disk_car->pread=io_redir_pread;
disk_car->clean=io_redir_clean;
}
{
struct info_io_redir *data=(struct info_io_redir *)disk_car->data;
list_redir_t *prev_redir=NULL;
list_redir_t *current_redir;
for(current_redir=data->list_redir;(current_redir!=NULL) && org_offset<current_redir->org_offset+current_redir->size;current_redir=current_redir->next)
prev_redir=current_redir;
if(current_redir!=NULL && org_offset>=current_redir->org_offset)
{
log_critical("io_redir_add_redir failed: already redirected\n");
return 1;
}
{
list_redir_t *new_redir;
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_add_redir: add redirection\n");
#endif
new_redir=(list_redir_t*)MALLOC(sizeof(*new_redir));
new_redir->org_offset=org_offset;
new_redir->size=size;
new_redir->new_offset=new_offset;
new_redir->mem=mem;
new_redir->next=current_redir;
if(prev_redir!=NULL)
prev_redir->next=new_redir;
else
data->list_redir=new_redir;
}
}
return 0;
}
int io_redir_del_redir(disk_t *disk_car, uint64_t org_offset)
{
if(disk_car->pread!=io_redir_pread)
{
log_critical("io_redir_del_redir: BUG, no redirection present.\n");
return 1;
}
{
struct info_io_redir *data=(struct info_io_redir *)disk_car->data;
list_redir_t *current_redir;
for(current_redir=data->list_redir;(current_redir!=NULL) && org_offset!=current_redir->org_offset;current_redir=current_redir->next);
if(current_redir!=NULL)
{
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_del_redir: remove redirection\n");
#endif
if(current_redir->prev!=NULL)
current_redir->prev->next=current_redir->next;
if(current_redir->next!=NULL)
current_redir->next->prev=current_redir->prev;
if(data->list_redir==current_redir)
data->list_redir=current_redir->next;
free(current_redir);
if(data->list_redir==NULL)
{
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_del_redir: uninstall functions\n");
#endif
memcpy(disk_car,data->disk_car,sizeof(*disk_car));
free(data->disk_car);
free(data);
}
return 0;
}
log_critical("io_redir_del_redir: redirection not found\n");
return 1;
}
}
static int io_redir_pread(disk_t *disk_car, void *buffer, const unsigned int count, const uint64_t offset)
{
struct info_io_redir *data=(struct info_io_redir *)disk_car->data;
uint64_t current_offset=offset;
unsigned int current_count=count;
list_redir_t *current_redir;
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_pread: count=%u offset=%llu\n", count, (long long unsigned) offset);
#endif
while(current_count!=0)
{
unsigned int read_size;
int res=0;
for(current_redir=data->list_redir;(current_redir!=NULL) &&
!(current_redir->org_offset<=offset && offset<current_redir->org_offset+current_redir->size);
current_redir=current_redir->next);
if(current_redir!=NULL)
{
if(current_redir->org_offset>current_offset)
{
/* Read data before redirection */
read_size=current_redir->org_offset-current_offset;
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_pread: read %u bytes before redirection\n",read_size);
#endif
res=data->disk_car->pread(data->disk_car, buffer, read_size, current_offset);
current_count-=read_size;
current_offset+=read_size;
buffer=(unsigned char*)buffer+read_size;
}
/* Redirection */
read_size=(current_count>current_redir->size?current_redir->size:current_count);
if(current_redir->mem!=NULL)
{
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_pread: copy %u bytes from memory\n",read_size);
#endif
memcpy(buffer, (const unsigned char*)current_redir->mem + current_offset - current_redir->org_offset, read_size);
res=read_size;
}
else
{
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_pread: read %u from another position\n",read_size);
#endif
res=data->disk_car->pread(data->disk_car, buffer, read_size, current_redir->new_offset + current_offset - current_redir->org_offset);
;
}
}
else
{
read_size=current_count;
#ifdef DEBUG_IO_REDIR
log_trace("io_redir_pread: normal read of %u bytes\n",read_size);
#endif
res=data->disk_car->pread(data->disk_car, buffer, read_size, current_offset);
}
if((unsigned)res!=read_size)
return res;
current_count-=read_size;
current_offset+=read_size;
buffer=(unsigned char*)buffer+read_size;
}
return count;
}
static int io_redir_clean(disk_t *disk_car)
{
if(disk_car->data)
{
struct info_io_redir *data=(struct info_io_redir *)disk_car->data;
data->disk_car->clean(data->disk_car);
free(data->disk_car);
free(disk_car->data);
disk_car->data=NULL;
}
return 0;
}