testdisk/src/fnctdsk.c
2020-11-01 13:29:29 +01:00

440 lines
13 KiB
C

/*
File: fnctdsk.c
Copyright (C) 1998-2005,2008 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 "types.h"
#include "common.h"
#include "fnctdsk.h"
#include "log.h"
#include "log_part.h"
#include "guid_cpy.h"
/*@
@ requires \valid(part);
@*/
static list_part_t *element_new(partition_t *part)
{
list_part_t *new_element=(list_part_t*)MALLOC(sizeof(*new_element));
new_element->part=part;
new_element->prev=new_element->next=NULL;
new_element->to_be_removed=0;
return new_element;
}
unsigned long int C_H_S2LBA(const disk_t *disk_car,const unsigned int C, const unsigned int H, const unsigned int S)
{
return ((unsigned long int)C * disk_car->geom.heads_per_cylinder + H) * disk_car->geom.sectors_per_head + S - 1;
}
uint64_t CHS2offset(const disk_t *disk_car,const CHS_t*CHS)
{
return (((uint64_t)CHS->cylinder * disk_car->geom.heads_per_cylinder + CHS->head) *
disk_car->geom.sectors_per_head + CHS->sector - 1) * disk_car->sector_size;
}
unsigned int offset2sector(const disk_t *disk_car, const uint64_t offset)
{
return ((offset / disk_car->sector_size) % disk_car->geom.sectors_per_head) + 1;
}
unsigned int offset2head(const disk_t *disk_car, const uint64_t offset)
{
return ((offset / disk_car->sector_size) / disk_car->geom.sectors_per_head) % disk_car->geom.heads_per_cylinder;
}
unsigned int offset2cylinder(const disk_t *disk_car, const uint64_t offset)
{
return ((offset / disk_car->sector_size) / disk_car->geom.sectors_per_head) / disk_car->geom.heads_per_cylinder;
}
void offset2CHS(const disk_t *disk_car,const uint64_t offset, CHS_t*CHS)
{
uint64_t pos=offset/disk_car->sector_size;
CHS->sector=(pos%disk_car->geom.sectors_per_head)+1;
pos/=disk_car->geom.sectors_per_head;
CHS->head=pos%disk_car->geom.heads_per_cylinder;
CHS->cylinder=pos/disk_car->geom.heads_per_cylinder;
}
void dup_partition_t(partition_t *dst, const partition_t *src)
{
#if 0
dst->part_offset=src->part_offset;
dst->part_size=src->part_size;
dst->boot_sector=src->boot_sector;
dst->boot_sector_size=src->boot_sector_size;
dst->blocksize=src->blocksize;
dst->part_type_i386=src->part_type_i386;
dst->part_type_sun=src->part_type_sun;
dst->part_type_mac=src->part_type_mac;
dst->part_type_xbox=src->part_type_xbox;
dst->part_type_gpt=src->part_type_gpt;
dst->upart_type=src->upart_type;
dst->status=src->status;
dst->order=src->order;
dst->errcode=src->errcode;
strncpy(dst->info,src->info,sizeof(dst->info));
strncpy(dst->fsname,src->name,sizeof(dst->fsname));
strncpy(dst->partname,src->name,sizeof(dst->partname));
dst->arch=src->arch;
#else
memcpy(dst, src, sizeof(*src));
#endif
}
list_disk_t *insert_new_disk_aux(list_disk_t *list_disk, disk_t *disk, disk_t **the_disk)
{
list_disk_t *tmp;
list_disk_t *prev=NULL;
list_disk_t *new_disk;
if(disk==NULL)
{
if(the_disk!=NULL)
*the_disk=NULL;
return list_disk;
}
/* Add it at the end if it doesn't already exist */
for(tmp=list_disk;tmp!=NULL;tmp=tmp->next)
{
if(tmp->disk->device!=NULL && disk->device!=NULL &&
strcmp(tmp->disk->device, disk->device)==0)
{
disk->clean(disk);
if(the_disk!=NULL)
*the_disk=tmp->disk;
return list_disk;
}
prev=tmp;
}
new_disk=(list_disk_t *)MALLOC(sizeof(*new_disk));
new_disk->disk=disk;
if(prev!=NULL)
{
prev->next=new_disk;
}
new_disk->prev=prev;
new_disk->next=NULL;
if(the_disk!=NULL)
*the_disk=disk;
return (list_disk!=NULL?list_disk:new_disk);
}
list_disk_t *insert_new_disk(list_disk_t *list_disk, disk_t *disk)
{
return insert_new_disk_aux(list_disk, disk, NULL);
}
list_part_t *insert_new_partition(list_part_t *list_part, partition_t *part, const int force_insert, int *insert_error)
{
list_part_t *prev=NULL;
list_part_t *next;
*insert_error=0;
for(next=list_part;;next=next->next)
{ /* prev new next */
if((next==NULL)||
(part->part_offset<next->part->part_offset) ||
(part->part_offset==next->part->part_offset &&
((part->part_size<next->part->part_size) ||
(part->part_size==next->part->part_size && (force_insert==0 || part->sb_offset < next->part->sb_offset)))))
{
if(force_insert==0 &&
(next!=NULL) &&
(next->part->part_offset==part->part_offset) &&
(next->part->part_size==part->part_size) &&
(next->part->part_type_i386==part->part_type_i386) &&
(next->part->part_type_mac==part->part_type_mac) &&
(next->part->part_type_sun==part->part_type_sun) &&
(next->part->part_type_xbox==part->part_type_xbox) &&
(next->part->upart_type==part->upart_type || part->upart_type==UP_UNK))
{ /*CGR 2004/05/31*/
if(next->part->status==STATUS_DELETED)
{
next->part->status=part->status;
}
*insert_error=1;
return list_part;
}
{ /* prev new_element next */
list_part_t *new_element;
new_element=element_new(part);
new_element->next=next;
new_element->prev=prev;
if(next!=NULL)
next->prev=new_element;
if(prev!=NULL)
{
prev->next=new_element;
return list_part;
}
return new_element;
}
}
prev=next;
}
}
int delete_list_disk(list_disk_t *list_disk)
{
list_disk_t *element_disk;
int write_used=0;
for(element_disk=list_disk;element_disk!=NULL;)
{
list_disk_t *element_disk_next=element_disk->next;
write_used|=element_disk->disk->write_used;
element_disk->disk->clean(element_disk->disk);
free(element_disk);
element_disk=element_disk_next;
}
return write_used;
}
list_part_t *sort_partition_list(list_part_t *list_part)
{
list_part_t *new_list_part=NULL;
list_part_t *element;
list_part_t *next;
for(element=list_part;element!=NULL;element=next)
{
int insert_error=0;
next=element->next;
new_list_part=insert_new_partition(new_list_part, element->part, 0, &insert_error);
if(insert_error>0)
free(element->part);
free(element);
}
return new_list_part;
}
list_part_t *gen_sorted_partition_list(const list_part_t *list_part)
{
list_part_t *new_list_part=NULL;
const list_part_t *element;
for(element=list_part;element!=NULL;element=element->next)
{
int insert_error=0;
if(element->part->status!=STATUS_DELETED)
new_list_part=insert_new_partition(new_list_part, element->part, 1, &insert_error);
}
return new_list_part;
}
/* Delete the list and its content */
void part_free_list(list_part_t *list_part)
{
list_part_t *element;
element=list_part;
while(element!=NULL)
{
list_part_t *next=element->next;
free(element->part);
free(element);
element=next;
}
}
/* Free the list but not its content */
void part_free_list_only(list_part_t *list_part)
{
list_part_t *element;
element=list_part;
while(element!=NULL)
{
list_part_t *next=element->next;
free(element);
element=next;
}
}
int is_part_overlapping(const list_part_t *list_part)
{
const list_part_t *element;
/* Test overlapping
Must be space between a primary/logical partition and a logical partition for an extended
*/
if(list_part==NULL)
return 0;
element=list_part;
while(1)
{
const list_part_t *next=element->next;
const partition_t *partition=element->part;
if(next==NULL)
return 0;
if( (partition->part_offset + partition->part_size - 1 >= next->part->part_offset) ||
((partition->status==STATUS_PRIM ||
partition->status==STATUS_PRIM_BOOT ||
partition->status==STATUS_LOG) &&
next->part->status==STATUS_LOG &&
partition->part_offset + partition->part_size - 1 + 1 >= next->part->part_offset))
return 1;
element=next;
}
}
void partition_reset(partition_t *partition, const arch_fnct_t *arch)
{
/* partition->lba=0; Don't reset lba, used by search_part */
partition->part_size=(uint64_t)0;
partition->sborg_offset=0;
partition->sb_offset=0;
partition->sb_size=0;
partition->blocksize=0;
partition->part_type_i386=P_NO_OS;
partition->part_type_sun=PSUN_UNK;
partition->part_type_mac=PMAC_UNK;
partition->part_type_xbox=PXBOX_UNK;
partition->part_type_gpt=GPT_ENT_TYPE_UNUSED;
guid_cpy(&partition->part_uuid, &GPT_ENT_TYPE_UNUSED);
partition->upart_type=UP_UNK;
partition->status=STATUS_DELETED;
partition->order=NO_ORDER;
partition->errcode=BAD_NOERR;
partition->fsname[0]='\0';
partition->partname[0]='\0';
partition->info[0]='\0';
partition->arch=arch;
}
partition_t *partition_new(const arch_fnct_t *arch)
{
partition_t *partition=(partition_t *)MALLOC(sizeof(*partition));
partition_reset(partition, arch);
return partition;
}
/*@
@ requires \valid_read(disk_car);
@ requires \valid_read(list_part);
@ assigns \nothing;
@*/
static unsigned int get_geometry_from_list_part_aux(const disk_t *disk_car, const list_part_t *list_part, const int verbose)
{
const list_part_t *element;
unsigned int nbr=0;
/*@ loop assigns element, nbr; */
for(element=list_part;element!=NULL;element=element->next)
{
CHS_t start;
CHS_t end;
offset2CHS(disk_car,element->part->part_offset,&start);
offset2CHS(disk_car,element->part->part_offset+element->part->part_size-1,&end);
if(start.sector==1 && start.head<=1)
{
nbr++;
if(end.head==disk_car->geom.heads_per_cylinder-1)
{
nbr++;
/* Doesn't check if end.sector==disk_car->CHS.sector */
}
}
}
#ifndef __FRAMAC__
if(nbr>0)
{
log_info("get_geometry_from_list_part_aux head=%u nbr=%u\n",
disk_car->geom.heads_per_cylinder, nbr);
if(verbose>1)
{
for(element=list_part;element!=NULL;element=element->next)
{
CHS_t start;
CHS_t end;
offset2CHS(disk_car,element->part->part_offset,&start);
offset2CHS(disk_car,element->part->part_offset+element->part->part_size-1,&end);
if(start.sector==1 && start.head<=1 && end.head==disk_car->geom.heads_per_cylinder-1)
{
log_partition(disk_car,element->part);
}
}
}
}
#endif
return nbr;
}
unsigned int get_geometry_from_list_part(const disk_t *disk_car, const list_part_t *list_part, const int verbose)
{
const unsigned int head_list[]={8,16,32,64,128,240,255,0};
unsigned int best_score;
unsigned int i;
unsigned int heads_per_cylinder=disk_car->geom.heads_per_cylinder;
disk_t *new_disk_car=(disk_t *)MALLOC(sizeof(*new_disk_car));
memcpy(new_disk_car,disk_car,sizeof(*new_disk_car));
best_score=get_geometry_from_list_part_aux(new_disk_car, list_part, verbose);
for(i=0; head_list[i]!=0; i++)
{
unsigned int score;
new_disk_car->geom.heads_per_cylinder=head_list[i];
score=get_geometry_from_list_part_aux(new_disk_car, list_part, verbose);
if(score >= best_score)
{
best_score=score;
heads_per_cylinder=new_disk_car->geom.heads_per_cylinder;
}
}
free(new_disk_car);
return heads_per_cylinder;
}
void size_to_unit(const uint64_t disk_size, char *buffer)
{
if(disk_size<(uint64_t)10*1024)
sprintf(buffer,"%u B", (unsigned)disk_size);
else if(disk_size<(uint64_t)10*1024*1024)
sprintf(buffer,"%u KB / %u KiB", (unsigned)(disk_size/1000), (unsigned)(disk_size/1024));
else if(disk_size<(uint64_t)10*1024*1024*1024)
sprintf(buffer,"%u MB / %u MiB", (unsigned)(disk_size/1000/1000), (unsigned)(disk_size/1024/1024));
else if(disk_size<(uint64_t)10*1024*1024*1024*1024)
sprintf(buffer,"%u GB / %u GiB", (unsigned)(disk_size/1000/1000/1000), (unsigned)(disk_size/1024/1024/1024));
else
sprintf(buffer,"%u TB / %u TiB", (unsigned)(disk_size/1000/1000/1000/1000), (unsigned)(disk_size/1024/1024/1024/1024));
}
void log_disk_list(list_disk_t *list_disk)
{
list_disk_t *element_disk;
/* save disk parameters to rapport */
log_info("Hard disk list\n");
for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
{
disk_t *disk=element_disk->disk;
log_info("%s, sector size=%u", disk->description(disk), disk->sector_size);
if(disk->model!=NULL)
log_info(" - %s", disk->model);
if(disk->serial_no!=NULL)
log_info(", S/N:%s", disk->serial_no);
if(disk->fw_rev!=NULL)
log_info(", FW:%s", disk->fw_rev);
log_info("\n");
}
log_info("\n");
}