/*
File: fat_adv.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 <ctype.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "types.h"
#include "common.h"
#include "fat.h"
#include "lang.h"
#include "fnctdsk.h"
#include "testdisk.h"
#include "intrf.h"
#include "intrfn.h"
#include "dir.h"
#include "dirpart.h"
#include "fat_dir.h"
#include "io_redir.h"
#include "log.h"
#define INTER_FAT_ASK_X 0
#define INTER_FAT_ASK_Y 23
static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
typedef struct sector_cluster_struct sector_cluster_t;
typedef struct info_offset_struct info_offset_t;
typedef struct cluster_offset_struct cluster_offset_t;
struct sector_cluster_struct
{
unsigned int sector;
unsigned int cluster;
};
struct info_offset_struct
{
unsigned long int offset;
unsigned int nbr;
unsigned int fat_type;
};
struct cluster_offset_struct
{
unsigned int cluster_size;
unsigned long int offset;
unsigned int nbr;
unsigned int first_sol;
};
static upart_type_t fat_find_info(disk_t *disk_car,unsigned int*reserved, unsigned int*fat_length, const partition_t *partition,const uint64_t max_offset,const int p_fat12,const int p_fat16,const int p_fat32,const int debug,const int dump_ind,const int interface, const unsigned int expert, unsigned int *fats);
static int fat_find_type(disk_t *disk_car,const partition_t *partition,const uint64_t max_offset,const int p_fat12,const int p_fat16,const int p_fat32,const int debug,const int dump_ind,const int interface,unsigned int *nbr_offset,info_offset_t *info_offset);
static unsigned int fat_find_fat_start(const unsigned char *buffer,const int p_fat12, const int p_fat16, const int p_fat32,unsigned long int*fat_offset, const unsigned int sector_size);
static upart_type_t no_of_cluster2part_type(const unsigned long int no_of_cluster);
static int create_fat_boot_sector(disk_t *disk_car, partition_t *partition, const unsigned int reserved, const int debug, const unsigned int dir_entries, const unsigned long int root_cluster, const unsigned int cluster_size, const unsigned int fat_length,const int interface, const upart_type_t upart_type, const unsigned int fats, char **current_cmd);
static unsigned int fat32_find_root_cluster(disk_t *disk_car,const partition_t *partition,const unsigned int cluster_size, const unsigned long int no_of_cluster, const unsigned int reserved, const unsigned int fat_length, const int interface, const int debug, const unsigned int expert, const unsigned int first_free_cluster, const unsigned int fats);
static int write_FAT_boot_code_aux(unsigned char *buffer);
static int find_cluster_size(disk_t *disk_car, partition_t *partition, const int debug, const int dump_ind,const int interface, unsigned int *cluster_size, unsigned long int *offset);
static int find_dir_entries(disk_t *disk_car,const partition_t *partition, const unsigned int offset,const int debug);
static int find_cluster_size_aux(const sector_cluster_t *sector_cluster, const unsigned int nbr_sector_cluster,unsigned int *cluster_size, unsigned long int *offset, const int debug, const unsigned long int part_size_in_sectors);
static int analyse_dir_entries(disk_t *disk_car,const partition_t *partition, const unsigned int offset, const int debug);
static int analyse_dir_entries2(disk_t *disk_car,const partition_t *partition, const unsigned int reserved, const unsigned int fat_length,const int debug, unsigned int root_size_max,const upart_type_t upart_type, const unsigned int fats);
static int calcul_cluster_size(const upart_type_t upart_type, const unsigned long int data_size, const unsigned int fat_length, const unsigned int sector_size);
static int check_entree(const unsigned char *entree);
static int fat32_create_rootdir(disk_t *disk_car,const partition_t *partition, const unsigned int reserved, const unsigned int fat_length, const unsigned int root_cluster, const unsigned int cluster_size, const int debug, file_data_t *rootdir_list, const unsigned int fats);
static void fat_date_unix2dos(int unix_date,unsigned short *mstime, unsigned short *msdate);
static upart_type_t select_fat_info(const info_offset_t *info_offset, const unsigned int nbr_offset,unsigned int*reserved, unsigned int*fat_length, const unsigned long int max_sector_offset, unsigned int *fats);
static unsigned long int get_subdirectory(disk_t *disk_car,const uint64_t hd_offset, const unsigned long int i);
/* The goal is to identify the begining of the two FAT
* TestDisk assumes that
* - fragmentation is low
* - free cluster are unusual in the beginning of a FAT
* - small files are unsual at the beginning of a FAT
* If fragmentation, EOC(End Of Cluster) or free cluster, increments nb_frag.
* */
/*
* 0 entree is free
* 1 entree is used
* 2 not an entry
* */
static int check_entree(const unsigned char *entree)
{
int i;
if((entree[0xB]&ATTR_EXT_MASK)==ATTR_EXT)
return 1;
/* log_trace("check_entree %02x\n",*(entree+0)); */
if(entree[0]==0)
{
for(i=0;i<0x20;i++)
if(*(entree+i)!='\0')
return 2;
return 0;
}
if(entree[0]==0x20)
return 2;
if(entree[0]==0xE5)
return 1;
for(i=0;i<8+3;i++)
{
if((*(entree+i)>=0x06 && *(entree+i)<=0x1f)||
(*(entree+i)>=0x3a && *(entree+i)<=0x3f)||
(*(entree+i)>='a' && *(entree+i)<='z'))
return 2;
switch(*(entree+i))
{
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x22:
case 0x2A:
case 0x2B:
case 0x2C:
case 0x2E:
case 0x2F:
case 0x5B:
case 0x5C:
case 0x5D:
case 0x7C:
/*log_trace("check_entree bad %c (%02x)\n",*(entree+i),*(entree+i)); */
return 2;
default:
/*log_trace("check_entree good %c (%02x)\n",*(entree+i),*(entree+i)); */
break;
}
}
return 1;
}
/* */
static unsigned long int get_subdirectory(disk_t *disk_car,const uint64_t hd_offset,const unsigned long int i)
{
unsigned char buffer[DEFAULT_SECTOR_SIZE];
if(disk_car->read(disk_car,sizeof(buffer), &buffer, hd_offset)!=0)
{
log_error("fat_dir, get_subdirectory(), can't read directory\n");
return 1;
}
/* dump_ncurses(buffer,DEFAULT_SECTOR_SIZE); */
/* 12345678123*/
if(memcmp(&buffer[0],". ",8+3)!=0)
return 1;
if((unsigned)((buffer[0x15]<<24)|(buffer[0x14]<<16)|(buffer[0x1B]<<8)|buffer[0x1A])!=i)
return 1;
/* 12345678123*/
if(memcmp(&buffer[0x20],".. ",8+3)!=0)
return 1;
return (buffer[0x35]<<24)+(buffer[0x34]<<16)+(buffer[0x3B]<<8)+buffer[0x3A];
}
static int ask_root_directory(disk_t *disk_car, const partition_t *partition, const file_data_t*dir_list, const unsigned long int cluster)
{
/* Return value
* -1: quit
* 1: back
* other: new cluster
* */
int car='A';
int quit=0;
int offset=0;
int pos_num=0;
const file_data_t *current_file;
const file_data_t *pos=dir_list;
WINDOW *window;
window=newwin(0,0,0,0); /* full screen */
aff_copy(window);
wmove(window,4,0);
aff_part(window,AFF_PART_ORDER,disk_car,partition);
wmove(window,6,0);
wdoprintf(window,"Answer Y(es), N(o) or A(bort interactive mode). N or A if not sure.");
curs_set(1);
do
{
int i;
for(i=0,current_file=dir_list;(current_file!=NULL) && (i<offset);current_file=current_file->next,i++);
for(i=offset;(current_file!=NULL) &&((i-offset)<INTER_DIR);i++,current_file=current_file->next)
{
struct tm *tm_p;
char str[11];
char datestr[80];
wmove(window,8+i-offset,0);
wclrtoeol(window); /* before addstr for BSD compatibility */
if(current_file==pos)
wattrset(window, A_REVERSE);
if(current_file->filestat.st_mtime!=0)
{
tm_p = localtime(¤t_file->filestat.st_mtime);
snprintf(datestr, sizeof(datestr),"%2d-%s-%4d %02d:%02d",
tm_p->tm_mday, monstr[tm_p->tm_mon],
1900 + tm_p->tm_year, tm_p->tm_hour,
tm_p->tm_min);
} else {
strncpy(datestr, " ",sizeof(datestr));
}
mode_string(current_file->filestat.st_mode,str);
wdoprintf(window, "%s %5u %5u ",
str, (unsigned int)current_file->filestat.st_uid, (unsigned int)current_file->filestat.st_gid);
wdoprintf(window, "%7llu", (long long unsigned int)current_file->filestat.st_size);
/* FIXME: screen overlaps due to long filename */
wdoprintf(window, " %s %s\n", datestr, current_file->name);
if(current_file==pos)
wattroff(window, A_REVERSE);
}
/* Clear the last line, useful if overlapping */
wmove(window,8+i-offset,0);
wclrtoeol(window); /* before addstr for BSD compatibility */
/* print the cluster in the loop, so */
/* the visible cursor will be at the end */
wmove(window,5,0);
wdoprintf(window,"Cluster %lu, Directory / found ? ", cluster);
wrefresh(window);
car=wgetch(window);
switch(car)
{
case 'a':
case 'A':
case 'y':
case 'Y':
case 'n':
case 'N':
quit=1;
break;
}
if(dir_list!=NULL)
{
switch(car)
{
case KEY_UP:
if(pos->prev!=NULL)
{
pos=pos->prev;
pos_num--;
}
if(pos_num<offset)
offset--;
break;
case KEY_DOWN:
if(pos->next!=NULL)
{
pos=pos->next;
pos_num++;
}
if(pos_num>=offset+INTER_DIR)
offset++;
break;
case KEY_PPAGE:
for(i=0;(i<INTER_DIR-1)&&(pos->prev!=NULL);i++)
{
pos=pos->prev;
pos_num--;
if(pos_num<offset)
offset--;
}
break;
case KEY_NPAGE:
for(i=0;(i<INTER_DIR-1)&&(pos->next!=NULL);i++)
{
pos=pos->next;
pos_num++;
if(pos_num>=offset+INTER_DIR)
offset++;
}
break;
}
}
} while(quit==0);
curs_set(0);
wdoprintf(window,"%c\n",car);
delwin(window);
(void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
touchwin(stdscr);
#endif
return toupper(car);
}
static unsigned int fat32_find_root_cluster(disk_t *disk_car,const partition_t *partition,const unsigned int cluster_size, const unsigned long int no_of_cluster,const unsigned int reserved, const unsigned int fat_length, const int interface, const int debug, const unsigned int expert, const unsigned int first_free_cluster, const unsigned int fats)
{
unsigned long int root_cluster=0;
const unsigned int start_data=reserved+fats*fat_length;
if(debug>0)
log_debug("fat32_find_root_cluster(cluster_size=%u,no_of_cluster=%lu,reserved=%u,fat_length=%u,expert=%u,first_free_cluster=%u)\n",cluster_size,no_of_cluster,reserved,fat_length,expert,first_free_cluster);
if(cluster_size==0)
return 0;
{
file_data_t *rootdir_list=NULL;
file_data_t *current_file=NULL;
unsigned int dir_nbr=0;
int interactive=1;
unsigned char buffer[cluster_size*disk_car->sector_size];
int ind_stop=FALSE;
if(interface)
{
wmove(stdscr,22,0);
wattrset(stdscr, A_REVERSE);
waddstr(stdscr," Stop ");
wattroff(stdscr, A_REVERSE);
}
for(root_cluster=2;(root_cluster<2+no_of_cluster)&&(ind_stop==0);root_cluster++)
{
unsigned long int percent=root_cluster*100/(2+no_of_cluster);
if(interface>0 && (root_cluster&0xfff)==0)
{
wmove(stdscr,9,0);
wclrtoeol(stdscr);
wdoprintf(stdscr,"Search root cluster %10lu/%lu %lu%%",root_cluster,2+no_of_cluster,percent);
wrefresh(stdscr);
ind_stop|=check_enter_or_s(stdscr);
}
if(disk_car->read(disk_car,cluster_size*disk_car->sector_size, &buffer, partition->part_offset+(uint64_t)(start_data+(root_cluster-2)*cluster_size)*disk_car->sector_size)==0)
{
if(debug>1)
{
log_trace("fat32_find_root_cluster test cluster=%lu\n",root_cluster);
/*
dump_ncurses(buffer,cluster_size*disk_car->sector_size);
*/
}
if((memcmp(&buffer[0],". ",8+3)==0) &&
(buffer[0xB]!=ATTR_EXT && (buffer[0xB]&ATTR_DIR)!=0))
{ /* Directory found */
unsigned long int cluster=(buffer[1*0x20+0x15]<<24)+(buffer[1*0x20+0x14]<<16)+
(buffer[1*0x20+0x1B]<<8)+buffer[1*0x20+0x1A];
if((memcmp(&buffer[0x20],".. ",8+3)==0) &&
(buffer[1*0x20+0xB]!=ATTR_EXT && (buffer[1*0x20+0xB]&ATTR_DIR)!=0) && (cluster==0)
&& (buffer[0x40]!=0)) /* First-level directory */
{
file_data_t *dir_list;
log_info("First-level directory found at cluster %lu\n",root_cluster);
/* dump_ncurses(buffer,cluster_size*disk_car->sector_size); */
dir_list=dir_fat_aux(buffer,cluster_size*disk_car->sector_size,cluster_size);
if(debug>0)
{
dir_aff_log(disk_car, partition, NULL, dir_list);
}
{
file_data_t *new_file=MALLOC(sizeof(*new_file));
memcpy(new_file,dir_list,sizeof(*new_file));
new_file->prev=current_file;
new_file->next=NULL;
if(current_file!=NULL)
current_file->next=new_file;
else
rootdir_list=new_file;
current_file=new_file;
snprintf(new_file->name,sizeof(new_file->name),"DIR%05u",++dir_nbr);
}
delete_list_file(dir_list);
}
}
else if( memcmp(&buffer[0*0x20],&buffer[1*0x20],0x20)!=0)
{ /* Potential root directory */
unsigned int i,found=1;
int etat=0,nb_subdir=0,nb_subdir_ok=0;
for(i=0;found && (i<cluster_size*disk_car->sector_size/0x20);i++)
{
int res=check_entree(&buffer[i*0x20]);
if(debug>2)
log_trace("fat32_find_root_cluster root_cluster=%lu i=%u etat=%d res=%d\n",root_cluster,i,etat,res);
switch(res)
{
case 0:
if(etat==0)
etat=1;
break;
case 1:
if(etat==1)
{
etat=2;
found=0;
}
break;
case 2:
found=0;
break;
}
if((buffer[i*0x20]!=DELETED_FLAG) && (buffer[i*0x20+0xB]!= ATTR_EXT && (buffer[i*0x20+0xB]&ATTR_DIR)!=0)) /* Test directory */
{
nb_subdir++;
}
}
for(i=0;found && (i<16*cluster_size);i++)
{
if((buffer[i*0x20]!=DELETED_FLAG) && (buffer[i*0x20+0xB]!= ATTR_EXT && (buffer[i*0x20+0xB]&ATTR_DIR)!=0)) /* Test directory */
{
unsigned long int cluster=(buffer[i*0x20+0x15]<<24)+(buffer[i*0x20+0x14]<<16)+
(buffer[i*0x20+0x1B]<<8)+buffer[i*0x20+0x1A];
/* log_trace("cluster %ld\n",cluster); */
if((cluster>2+no_of_cluster)||(get_subdirectory(disk_car,partition->part_offset+(uint64_t)(start_data+(cluster-2)*cluster_size)*disk_car->sector_size,cluster)!=0))
{
/* if(debug) */
/* log_trace("failed with %s\n",&buffer[i*0x20]); */
}
else
nb_subdir_ok++;
}
}
if(found)
{
if((nb_subdir_ok>nb_subdir*0.90)&&(nb_subdir>=3))
{
unsigned long int new_root_cluster=root_cluster;
unsigned long int tmp=root_cluster;
int back=0; /* To avoid an endless loop... */
/* Il faut ajouter un parcours arriere de la FAT
* car on localise le dernier cluster du root_cluster */
if(debug>0)
log_debug("cluster %lu, etat=%d, found=%d,nb_subdir=%d,nb_subdir_ok=%d\n",root_cluster,etat,found,nb_subdir,nb_subdir_ok);
do
{
new_root_cluster=tmp;
tmp=fat32_get_prev_cluster(disk_car,partition,reserved,new_root_cluster,no_of_cluster);
if(debug)
log_debug("prev cluster(%lu)=>%lu\n",new_root_cluster,tmp);
if(tmp)
{
/* Check cluster number */
if((tmp<2) || (tmp>=2+no_of_cluster))
{
log_error("bad cluster number\n");
return new_root_cluster;
}
/* Read the cluster */
if(disk_car->read(disk_car,cluster_size*disk_car->sector_size, &buffer, partition->part_offset+(uint64_t)(start_data+(tmp-2)*cluster_size)*disk_car->sector_size)!=0)
{
log_critical("cluster can't be read\n");
return new_root_cluster;
}
/* Check if this cluster is a directory structure. FAT can be damaged */
for(i=0;i<16*cluster_size;i++)
{
if(check_entree(&buffer[i*0x20])!=1)
{
log_error("cluster data is not a directory structure\n");
return new_root_cluster;
}
}
}
} while(tmp && (++back<10));
return new_root_cluster;
}
else
{
if(debug>1)
{
log_debug("cluster %lu, etat=%d, found=%d,nb_subdir=%d,nb_subdir_ok=%d\n",
root_cluster,etat,found,nb_subdir,nb_subdir_ok);
}
}
{
file_data_t *dir_list;
dir_list=dir_fat_aux(buffer,cluster_size*disk_car->sector_size,cluster_size);
if(dir_list!=NULL && (dir_list->next==NULL || dir_list->filestat.st_ino!=dir_list->next->filestat.st_ino))
{
int test_date=1;
if(debug>0)
{
log_debug("Potential root_cluster %lu\n",root_cluster);
test_date=dir_aff_log(disk_car, partition, NULL, dir_list);
}
if(interface && interactive>0 && test_date>0)
{
switch(ask_root_directory(disk_car,partition,dir_list,root_cluster))
{
case c_YES:
delete_list_file(dir_list);
return root_cluster;
case 'A':
interactive=0;
break;
default:
break;
}
}
}
delete_list_file(dir_list);
}
}
}
}
}
if(ind_stop!=0)
log_info("Search root cluster stopped: %10lu (2..%lu)\n",root_cluster,no_of_cluster+1);
else
log_error("Search root cluster failed\n");
root_cluster=0;
if(rootdir_list==NULL)
{
log_warning("No first-level directory found.\n");
}
else
{
dir_aff_log(disk_car, partition, NULL, rootdir_list);
/* && (ind_stop==0) */
if(interface && (expert>0))
{
if(ask_confirmation("Create a new root cluster with %u first-level directories (Expert only) (Y/N)",dir_nbr)!=0 && ask_confirmation("Write root cluster, confirm ? (Y/N)")!=0)
{
root_cluster=first_free_cluster;
fat32_create_rootdir(disk_car, partition, reserved, fat_length, root_cluster, cluster_size, debug, rootdir_list, fats);
}
}
delete_list_file(rootdir_list);
}
}
return root_cluster;
}
static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
/* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
static void fat_date_unix2dos(int unix_date,unsigned short *mstime, unsigned short *msdate)
{
int day,year,nl_day,month;
/* unix_date -= sys_tz.tz_minuteswest*60; */
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
if (unix_date < 315532800)
unix_date = 315532800;
*mstime = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
(((unix_date/3600) % 24) << 11);
day = unix_date/86400-3652;
year = day/365;
if ((year+3)/4+365*year > day) year--;
day -= (year+3)/4+365*year;
if (day == 59 && !(year & 3)) {
nl_day = day;
month = 2;
}
else {
nl_day = (year & 3) || day <= 59 ? day : day-1;
for (month = 0; month < 12; month++)
if (day_n[month] > nl_day) break;
}
*msdate = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
}
static int file2entry(struct msdos_dir_entry *de, const file_data_t *current_file)
{
unsigned int i,j;
/* Name */
for(i=0;(i<8)&&(current_file->name[i]!='.')&&(current_file->name[i]!='\0');i++)
{
de->name[i]=current_file->name[i];
}
for(j=i;j<8;j++)
{
de->name[j]=' ';
}
/* Extension */
for(;(current_file->name[i]!='.')&&(current_file->name[i]!='\0');i++);
for(j=0;(j<3)&&(current_file->name[i]!='\0');j++)
{
de->ext[j]=current_file->name[i];
}
for(;j<3;j++)
{
de->ext[j]=' ';
}
de->attr=(LINUX_S_ISDIR(current_file->filestat.st_mode)!=0?ATTR_DIR:ATTR_NONE);
fat_date_unix2dos(current_file->filestat.st_mtime,&de->time,&de->date);
de->start=(current_file->filestat.st_ino);
de->starthi=(current_file->filestat.st_ino>>16);
de->size=current_file->filestat.st_size;
return 0;
}
static int fat32_create_rootdir(disk_t *disk_car,const partition_t *partition, const unsigned int reserved, const unsigned int fat_length, const unsigned int root_cluster, const unsigned int cluster_size, const int debug, file_data_t *rootdir_list, const unsigned int fats)
{
const unsigned int start_data=reserved+fats*fat_length;
unsigned int current_entry=0;
unsigned int cluster;
unsigned char *buffer;
file_data_t *current_file;
if(debug>0)
{
log_trace("fat32_create_rootdir(reserved=%u,fat_length=%u,root_cluster=%u,cluster_size=%u)\n",reserved,fat_length,root_cluster,cluster_size);
}
cluster=root_cluster;
buffer=MALLOC(disk_car->sector_size*cluster_size);
memset(buffer,0,disk_car->sector_size*cluster_size);
for(current_file=rootdir_list;current_file!=NULL;current_file=current_file->next)
{
file2entry((struct msdos_dir_entry*)buffer+current_entry,current_file);
if(++current_entry==(disk_car->sector_size*cluster_size/sizeof(struct msdos_dir_entry)))
{
unsigned int next_cluster;
if(disk_car->write(disk_car,disk_car->sector_size*cluster_size, buffer, partition->part_offset+(uint64_t)(start_data+(cluster-2)*cluster_size)*disk_car->sector_size)!=0)
{
display_message("Write error: Can't create FAT32 root cluster.\n");
}
current_entry=0;
memset(buffer,0,disk_car->sector_size*cluster_size);
/* FIXME need to write fat32_get_next_free_cluster */
next_cluster=cluster++;
set_next_cluster(disk_car,partition,UP_FAT32,reserved,cluster,next_cluster);
set_next_cluster(disk_car,partition,UP_FAT32,reserved+fat_length,cluster,next_cluster);
cluster=next_cluster;
}
}
if(disk_car->write(disk_car,disk_car->sector_size*cluster_size, buffer, partition->part_offset+(uint64_t)(start_data+(cluster-2)*cluster_size)*disk_car->sector_size)!=0)
{
display_message("Write error: Can't create FAT32 root cluster.\n");
}
set_next_cluster(disk_car,partition,UP_FAT32,reserved,cluster,FAT32_EOC);
set_next_cluster(disk_car,partition,UP_FAT32,reserved+fat_length,cluster,FAT32_EOC);
#ifdef DEBUG
{
file_data_t *dir_list;
dir_list=dir_fat_aux(buffer,disk_car->sector_size*cluster_size,cluster_size);
dir_aff_log(disk_car, partition, NULL, dir_list);
delete_list_file(dir_list);
}
#endif
free(buffer);
return 0;
}
static int find_dir_entries(disk_t *disk_car,const partition_t *partition, const unsigned int offset,const int debug)
{
unsigned char buffer[disk_car->sector_size];
unsigned int i,j;
int etat=0;
int sector_etat0=0;
uint64_t hd_offset;
hd_offset=partition->part_offset+(uint64_t)offset*disk_car->sector_size;
for(i=0;(i<200)&&(etat!=2)&&(i<offset);i++)
{
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, hd_offset)!=0)
{
log_error("dir_entries: read error, dir_entries>=%u (%u sectors)\n",i*(disk_car->sector_size/32),i);
}
else
{
for(j=disk_car->sector_size/32-1;j>0;j--)
{
if(debug>1)
{
log_debug("find_dir_entries sector=%u entree=%d etat=%d\n",offset-i,j,etat);
}
if(etat==0)
{
switch(check_entree(&buffer[j*0x20]))
{
case 0:
break;
case 1:
sector_etat0=i;
etat=1;
break;
case 2:
return 0;
}
} else {
switch(check_entree(&buffer[j*0x20]))
{
case 1:
etat=1;
break;
case 0:
case 2:
return (i-1)*(disk_car->sector_size/32);
}
}
}
}
hd_offset-=disk_car->sector_size;
}
if((i==200) || (i==offset))
return 0;
return (i-1)*(disk_car->sector_size/32);
}
static int analyse_dir_entries(disk_t *disk_car,const partition_t *partition, const unsigned int offset,const int debug)
{
unsigned char buffer[disk_car->sector_size];
unsigned int i,j;
int etat=0;
unsigned int sector_etat1=0;
uint64_t hd_offset;
hd_offset=partition->part_offset+(uint64_t)offset*disk_car->sector_size;
for(i=0;(i<200)&&(etat!=2);i++)
{
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, hd_offset)!=0)
{
log_error("dir_entries: read error, dir_entries>=%u (%u sectors)\n",i*(disk_car->sector_size/32),i);
}
else
{
for(j=0;j<(disk_car->sector_size/32);j++)
{
if(check_entree(&buffer[j*0x20])==0)
{
if(etat==0)
{
if(i*(disk_car->sector_size/32)+j>0)
{
etat=1;
sector_etat1=i;
if(debug>0)
log_debug("dir_entries 0->1 %d\n",i*(disk_car->sector_size/32)+j);
} else {
return 0;
}
}
}
else
if(etat==1)
{
if(i==sector_etat1)
{
return 0;
} else {
etat=2;
if(debug>0)
log_debug("dir_entries 1->2 %d\n",i*(disk_car->sector_size/32)+j);
}
}
}
}
hd_offset+=disk_car->sector_size;
}
if(i==200)
return 0;
return (i-1)*(disk_car->sector_size/32);
}
static int analyse_dir_entries2(disk_t *disk_car,const partition_t *partition, const unsigned int reserved, const unsigned int fat_length,const int debug, unsigned int root_size_max,const upart_type_t upart_type, const unsigned int fats)
{
file_data_t *current_file;
file_data_t *dir_list=NULL;
unsigned int nbr_sector;
unsigned char *buffer_dir;
if(root_size_max==0)
{
root_size_max=4096;
}
nbr_sector=(root_size_max+(disk_car->sector_size/32)-1)/(disk_car->sector_size/32);
buffer_dir=(unsigned char *)MALLOC(disk_car->sector_size*nbr_sector);
if(disk_car->read(disk_car, nbr_sector*disk_car->sector_size, buffer_dir, partition->part_offset+(uint64_t)(reserved+fats*fat_length)*disk_car->sector_size)!=0)
{
log_error("FAT 1x can't read root directory\n");
free(buffer_dir);
return 0;
}
{
unsigned long int start_data=reserved+fats*fat_length+(root_size_max+(disk_car->sector_size/32)-1)/(disk_car->sector_size/32);
unsigned int cluster_size=calcul_cluster_size(upart_type,partition->part_size/disk_car->sector_size-start_data,fat_length,disk_car->sector_size);
dir_list=dir_fat_aux(buffer_dir,disk_car->sector_size*nbr_sector,cluster_size);
}
if(debug>1)
{
dir_aff_log(disk_car, partition, NULL, dir_list);
}
for(current_file=dir_list;(current_file!=NULL)&&(LINUX_S_ISDIR(current_file->filestat.st_mode)==0);current_file=current_file->next);
if(current_file!=NULL)
{
unsigned long int new_inode=current_file->filestat.st_ino;
unsigned int dir_entries;
if(debug>1)
{
log_debug("Directory %s used inode=%lu\n",current_file->name,new_inode);
}
for(dir_entries=(disk_car->sector_size/32);dir_entries<=root_size_max;dir_entries+=(disk_car->sector_size/32))
{
unsigned long int start_data=reserved+fats*fat_length+(dir_entries+(disk_car->sector_size/32)-1)/(disk_car->sector_size/32);
unsigned int cluster_size=calcul_cluster_size(upart_type,partition->part_size/disk_car->sector_size-start_data,fat_length,disk_car->sector_size);
if(debug>1)
{
log_debug("dir_entries %u, cluster_size %u\n",dir_entries,cluster_size);
}
if(disk_car->read(disk_car, disk_car->sector_size, buffer_dir, partition->part_offset+(uint64_t)(start_data+(new_inode-2)*cluster_size)*disk_car->sector_size)==0)
{
if((memcmp(&buffer_dir[0],". ",8+3)==0)&&(memcmp(&buffer_dir[0x20],".. ",8+3)==0))
{
unsigned long int cluster=(buffer_dir[0*0x20+0x15]<<24)+(buffer_dir[0*0x20+0x14]<<16)+
(buffer_dir[0*0x20+0x1B]<<8)+buffer_dir[0*0x20+0x1A];
unsigned long int cluster_prev=(buffer_dir[1*0x20+0x15]<<24)+(buffer_dir[1*0x20+0x14]<<16)+
(buffer_dir[1*0x20+0x1B]<<8)+buffer_dir[1*0x20+0x1A];
if(debug>1)
{
log_debug("Cluster %lu, directory .. found link to %lu\n",cluster,cluster_prev);
}
if(cluster_prev==0 && cluster==new_inode)
{
free(buffer_dir);
delete_list_file(dir_list);
return ((dir_entries+(disk_car->sector_size/32)-1)/(disk_car->sector_size/32))*(disk_car->sector_size/32);
}
}
}
}
}
else
{
log_warning("No directory found\n");
}
free(buffer_dir);
delete_list_file(dir_list);
return root_size_max;
}
static int create_fat_boot_sector(disk_t *disk_car, partition_t *partition, const unsigned int reserved, const int debug, const unsigned int dir_entries, const unsigned long int root_cluster, const unsigned int cluster_size, const unsigned int fat_length,const int interface, const upart_type_t upart_type, const unsigned int fats, char **current_cmd)
{
unsigned char orgboot[3*disk_car->sector_size];
unsigned char newboot[3*disk_car->sector_size];
struct fat_boot_sector *org_fat_header=(struct fat_boot_sector *)&orgboot;
struct fat_boot_sector *fat_header=(struct fat_boot_sector *)&newboot;
int error=0;
unsigned long int part_size=0;
if(disk_car->read(disk_car,3*disk_car->sector_size, &orgboot, partition->part_offset)!=0)
{
log_error("create_fat_boot_sector: Can't read old boot sector\n");
memset(&orgboot,0,3*disk_car->sector_size);
}
memcpy(&newboot,&orgboot,3*disk_car->sector_size);
if((le16(fat_header->marker)!=0xAA55)||
!((fat_header->ignored[0]==0xeb && fat_header->ignored[2]==0x90)||fat_header->ignored[0]==0xe9))
{
write_FAT_boot_code_aux(newboot);
}
fat_header->sector_size[0]=disk_car->sector_size & 0xFF;
fat_header->sector_size[1]=disk_car->sector_size >>8;
fat_header->fats=fats;
fat_header->media=0xF8;
fat_header->secs_track=le16(disk_car->CHS.sector);
fat_header->heads=le16(disk_car->CHS.head+1);
fat_header->marker=le16(0xAA55);
if(!((fat_header->ignored[0]==0xeb&&fat_header->ignored[2]==0x90)||fat_header->ignored[0]==0xe9))
{
fat_header->ignored[0]=0xeb;
fat_header->ignored[2]=0x90;
}
/* I have seen a FAT32 partition that Win98 2nd edition was unable to read
* because this name was missing! */
if(memcmp(fat_header->system_id,"MSDOS5.0",8) &&
memcmp(fat_header->system_id,"MSWIN4.0",8) &&
memcmp(fat_header->system_id,"MSWIN4.1",8))
memcpy(fat_header->system_id,"MSWIN4.1",8);
/* FIXME, need to know where the extended or logical partition start */
if(partition->status==STATUS_LOG)
fat_header->hidden=le32(disk_car->CHS.sector);
else
fat_header->hidden=le32((partition->part_offset/disk_car->sector_size));
fat_header->cluster_size=cluster_size;
fat_header->reserved=le16(reserved);
/* The filesystem size can be smaller than the partition size */
switch(upart_type)
{
case UP_FAT12:
part_size=le16(fat_header->reserved)+fats*fat_length+dir_entries*32/disk_car->sector_size+cluster_size*(fat_length*disk_car->sector_size*2/3-2);
break;
case UP_FAT16:
part_size=le16(fat_header->reserved)+fats*fat_length+dir_entries*32/disk_car->sector_size+cluster_size*(fat_length*(disk_car->sector_size/2)-2);
break;
case UP_FAT32:
part_size=le16(fat_header->reserved)+fats*fat_length+cluster_size*(fat_length*(disk_car->sector_size/4)-2);
break;
default:
log_critical("create_fat_boot_sector: severe error\n");
exit(1);
}
if(part_size>partition->part_size/disk_car->sector_size)
part_size=partition->part_size/disk_car->sector_size;
if(part_size>0xFFFF)
{
fat_header->sectors[0]=0;
fat_header->sectors[1]=0;
fat_header->total_sect=le32(part_size);
}
else
{
fat_header->sectors[1]=part_size>>8;
fat_header->sectors[0]=part_size;
fat_header->total_sect=le32(0);
}
switch(upart_type)
{
case UP_FAT12:
if((fat_length==0) || (dir_entries==0))
error=1;
if((newboot[36]<0x80)||(newboot[36]>0x88))
newboot[36]=0x80; /* BS_DrvNum=0x80 */
newboot[37]=0; /* BS_Reserved1=0 */
newboot[38]=0x29; /* Boot sig=0x29 */
if(memcmp(newboot+FAT_NAME2,"FAT32",5)==0)
memcpy(newboot+FAT_NAME2, " ",8);
memcpy(newboot+FAT_NAME1,"FAT12 ",8);
fat_header->fat_length=le16(fat_length);
fat_header->dir_entries[1]=dir_entries>>8;
fat_header->dir_entries[0]=dir_entries;
if(check_volume_name((const char*)&newboot[FAT1X_PART_NAME],11))
newboot[FAT1X_PART_NAME]='\0';
break;
case UP_FAT16:
if((fat_length==0) || (dir_entries==0))
error=1;
if((newboot[36]<0x80)||(newboot[36]>0x88))
newboot[36]=0x80; /* BS_DrvNum */
newboot[37]=0; /* BS_Reserved1=0 */
newboot[38]=0x29; /* Boot sig=0x29 */
if(memcmp(newboot+FAT_NAME2,"FAT32",5)==0)
memcpy(newboot+FAT_NAME2, " ",8);
memcpy(newboot+FAT_NAME1,"FAT16 ",8);
fat_header->fat_length=le16(fat_length);
fat_header->dir_entries[1]=dir_entries>>8;
fat_header->dir_entries[0]=dir_entries;
if(check_volume_name((const char*)&newboot[FAT1X_PART_NAME],11))
newboot[FAT1X_PART_NAME]='\0';
break;
case UP_FAT32:
if((fat_length==0) || (root_cluster==0))
error=1;
fat_header->fat_length=le16(0);
fat_header->dir_entries[0]=0;
fat_header->dir_entries[1]=0;
fat_header->fat32_length=le32(fat_length);
/*
Bits 0-3 -- Zero-based number of active FAT. Only valid if mirroring
is disabled.
Bits 4-6 -- Reserved.
Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs.
-- 1 means only one FAT is active; it is the one referenced
in bits 0-3.
Bits 8-15 -- Reserved.
*/
fat_header->flags=le16(0);
fat_header->version[0]=0;
fat_header->version[1]=0;
fat_header->root_cluster=le32(root_cluster);
/* Sector number of FSINFO structure in the reserved area of the FAT32 volume. */
fat_header->info_sector=le16(1);
fat_header->backup_boot=le16(6);
memset(&fat_header->BPB_Reserved,0,sizeof(fat_header->BPB_Reserved));
if((fat_header->BS_DrvNum<0x80)||(fat_header->BS_DrvNum>0x87))
fat_header->BS_DrvNum=0x80;
fat_header->BS_Reserved1=0;
fat_header->BS_BootSig=0x29;
if((memcmp(newboot+FAT_NAME1,"FAT12",5)==0) ||(memcmp(newboot+FAT_NAME1,"FAT16",5)==0))
memcpy(newboot+FAT_NAME1," ",8);
memcpy(fat_header->BS_FilSysType, "FAT32 ",8);
newboot[0x1FC]=0x00; /* part of the signature */
newboot[0x1FD]=0x00;
memset(&newboot[disk_car->sector_size],0,2*disk_car->sector_size);
newboot[disk_car->sector_size]='R'; /* Signature RRaA */
newboot[disk_car->sector_size+1]='R';
newboot[disk_car->sector_size+2]='a';
newboot[disk_car->sector_size+3]='A';
newboot[disk_car->sector_size+0x1E4]='r'; /* Signature rrAa */
newboot[disk_car->sector_size+0x1E5]='r';
newboot[disk_car->sector_size+0x1E6]='A';
newboot[disk_car->sector_size+0x1E7]='a';
/* Don't set the number of free cluster or the next free cluster */
newboot[disk_car->sector_size+0x1E8]=0xFF; /* 488: Free clusters on disk */
newboot[disk_car->sector_size+0x1E9]=0xFF;
newboot[disk_car->sector_size+0x1EA]=0xFF;
newboot[disk_car->sector_size+0x1EB]=0xFF;
newboot[disk_car->sector_size+0x1EC]=0xFF; /* 492: Next available clusters */
newboot[disk_car->sector_size+0x1ED]=0xFF;
newboot[disk_car->sector_size+0x1EE]=0xFF;
newboot[disk_car->sector_size+0x1EF]=0xFF;
newboot[disk_car->sector_size+0x1FC]=0x00; /* End of Sector signature */
newboot[disk_car->sector_size+0x1FD]=0x00;
newboot[disk_car->sector_size+0x1FE]=0x55;
newboot[disk_car->sector_size+0x1FF]=0xAA;
newboot[2*disk_car->sector_size+0x1FC]=0x00; /* End of Sector signature */
newboot[2*disk_car->sector_size+0x1FD]=0x00;
newboot[2*disk_car->sector_size+0x1FE]=0x55;
newboot[2*disk_car->sector_size+0x1FF]=0xAA;
if(check_volume_name((const char*)&newboot[FAT32_PART_NAME],11))
newboot[FAT32_PART_NAME]='\0';
break;
default:
log_critical("create_fat_boot_sector: severe error\n");
exit(1);
}
if(memcmp(newboot,orgboot,1*DEFAULT_SECTOR_SIZE)) /* Only compare the first sector */
{
log_warning(" New / Current boot sector");
log_fat2_dump(fat_header,org_fat_header,upart_type,disk_car->sector_size);
log_warning("Extrapolated boot sector and current boot sector are different.\n");
}
else
{
log_info("Extrapolated boot sector and current boot sector are identical.\n");
}
if(interface)
{
struct MenuItem menuSaveBoot[]=
{
{ 'D', "Dump", "Dump sector" },
{ 'L', "List", "List directories and files" },
{ 'W', "Write","Write boot"},
{ 'Q',"Quit","Quit this section"},
{ 0, NULL, NULL }
};
const char *options="DLQ";
int do_write=0;
int do_exit=0;
int no_confirm=0;
do
{
int command;
do_exit=0;
aff_copy(stdscr);
wmove(stdscr,4,0);
wdoprintf(stdscr,"%s",disk_car->description(disk_car));
mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG);
wmove(stdscr,6,0);
aff_part(stdscr,AFF_PART_ORDER,disk_car,partition);
wmove(stdscr,8,0);
if(memcmp(newboot,orgboot,DEFAULT_SECTOR_SIZE)) /* Only compare the first sector */
{
dump_2fat_info(fat_header, org_fat_header, upart_type,disk_car->sector_size);
wdoprintf(stdscr,"Extrapolated boot sector and current boot sector are different.\n");
if(error)
{
wdoprintf(stdscr,"Warning: Extrapolated boot sector have incorrect values.\n");
log_error("Warning: Extrapolated boot sector have incorrect values.\n");
}
options="DLWQ";
}
else
{
dump_fat_info(fat_header, upart_type,disk_car->sector_size);
wdoprintf(stdscr,"Extrapolated boot sector and current boot sector are identical.\n");
}
if(*current_cmd!=NULL)
{
command='Q';
while(*current_cmd[0]==',')
(*current_cmd)++;
if(strncmp(*current_cmd,"list",4)==0)
{
(*current_cmd)+=4;
command='L';
}
else if(strncmp(*current_cmd,"dump",4)==0)
{
(*current_cmd)+=4;
command='D';
}
else if(strncmp(*current_cmd,"noconfirm",9)==0)
{
command=0; /* do nothing */
no_confirm=1;
(*current_cmd)+=9;
}
else if(strncmp(*current_cmd,"write",5)==0)
{
command='W';
(*current_cmd)+=5;
}
screen_buffer_to_log();
}
else
command=wmenuSelect(stdscr,INTER_DUMP_Y, INTER_DUMP_X, menuSaveBoot,8,options,MENU_HORIZ|MENU_BUTTON, 1);
switch(command)
{
case 'w':
case 'W':
if(strchr(options,'W')!=NULL)
do_write=1;
break;
case 'd':
case 'D':
if(strchr(options,'D')!=NULL)
{
WINDOW *window=newwin(0,0,0,0); /* full screen */
keypad(window, TRUE); /* Need it to get arrow key */
aff_copy(window);
wmove(window,4,0);
wdoprintf(window,"%s",disk_car->description(disk_car));
wmove(window,5,0);
aff_part(window,AFF_PART_ORDER,disk_car,partition);
log_info(" Rebuild Boot sector Boot sector\n");
mvwaddstr(window,6,0, " Rebuild Boot sector Boot sector");
dump2(window,newboot,orgboot, (unsigned int)(upart_type==UP_FAT32?3*disk_car->sector_size:DEFAULT_SECTOR_SIZE));
delwin(window);
(void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
touchwin(stdscr);
#endif
}
break;
case 'l':
case 'L':
{
/* TODO: use redirector and dir_partition_fat */
//dir_partition_fat_aux(stdscr,disk_car, partition, debug, fat_header,upart_type,current_cmd);
const upart_type_t old_upart_type=upart_type;
partition->upart_type=upart_type;
io_redir_add_redir(disk_car,partition->part_offset,3*disk_car->sector_size,0,newboot);
dir_partition(disk_car, partition, debug, current_cmd);
io_redir_del_redir(disk_car,partition->part_offset);
partition->upart_type=old_upart_type;
}
break;
case 'q':
case 'Q':
do_exit=1;
break;
}
} while(do_write==0 && do_exit==0);
if(do_write!=0 && (no_confirm!=0 || ask_confirmation("Write FAT boot sector, confirm ? (Y/N)")!=0))
{
int err=0;
log_info("Write new boot!\n");
/* Write boot sector and backup boot sector */
if(upart_type==UP_FAT32)
{
if(disk_car->write(disk_car,3*disk_car->sector_size, &newboot, partition->part_offset)!=0 ||
disk_car->write(disk_car,3*disk_car->sector_size, &newboot, partition->part_offset+(uint64_t)le16(fat_header->backup_boot)*disk_car->sector_size)!=0)
err=1;
}
else
{
if(disk_car->write(disk_car,DEFAULT_SECTOR_SIZE, &newboot, partition->part_offset)!=0)
err=1;
}
if(err==1)
{
display_message("Write error: Can't write new FAT boot sector\n");
}
/* TestDisk don't correct the filesystem, use another utility */
}
else
log_info("Don't write new boot!\n");
}
return 0;
}
static int calcul_cluster_size(const upart_type_t upart_type, const unsigned long int data_size, const unsigned int fat_length, const unsigned int sector_size)
{
/* log_info("calcul_cluster_size data_size=%lu, fat_length=%u, sector_size=%u\n",data_size,fat_length,sector_size); */
if(fat_length==0)
return 0;
switch(upart_type)
{
case UP_FAT12:
return up2power(data_size/(fat_length*sector_size*2/3-1));
case UP_FAT16:
return up2power(data_size/(fat_length*sector_size/2-1));
case UP_FAT32:
return up2power(data_size/(fat_length*sector_size/4-1));
default:
log_critical("calcul_cluster_size: severe error\n");
return 0;
}
}
static unsigned int fat_find_fat_start(const unsigned char *buffer,const int p_fat12, const int p_fat16, const int p_fat32,unsigned long int*fat_offset, const unsigned int sector_size)
{
/* TODO: handle limited size of info_offset */
info_offset_t info_offset[sector_size];
unsigned int nbr_offset=0;
int have_fat_signature=0;
if(p_fat12!=0)
{
unsigned int i;
unsigned int low;
unsigned int high;
i=0;
high=0;
low=0;
while(high<(sector_size-1))
{
unsigned long int cluster=0;
if(low==0)
cluster=((buffer[high+1] & 0x0F) <<8) | buffer[high];
else
cluster=(buffer[high+1] <<4) | ((buffer[high]&0xF0)>>4);
if((cluster!=0) && ((cluster&0x0ff8)!=(unsigned)0x0ff8) && (((cluster-i-1)*3)%(2*sector_size)==0))
{
unsigned int j;
for(j=0;(j<nbr_offset) &&
(info_offset[j].offset!=(cluster-i-1)*3/(2*sector_size) || info_offset[j].fat_type!=12);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=(cluster-i-1)*3/(2*sector_size);
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=12;
nbr_offset++;
}
}
if(low==0)
low=1;
else
{
high++;
low=0;
}
high++;
i++;
}
i=1;
high=1;
low=0;
while(high<(sector_size-1))
{
unsigned long int cluster=0;
if(low==0)
cluster=((buffer[high+1] & 0x0F) <<8) | buffer[high];
else
cluster=(buffer[high+1] <<4) | ((buffer[high]&0xF0)>>4);
if((cluster!=0) && ((cluster&0x0ff8)!=(unsigned)0x0ff8) && (((cluster-i-1)*3+1)%(2*sector_size)==0))
{
unsigned int j;
for(j=0;(j<nbr_offset) &&
(info_offset[j].offset!=((cluster-i-1)*3+1)/(2*sector_size) || info_offset[j].fat_type!=12);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=((cluster-i-1)*3+1)/(2*sector_size);
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=12;
nbr_offset++;
}
}
if(low==0)
low=1;
else
{
high++;
low=0;
}
high++;
i++;
}
i=1;
high=0;
low=1;
while(high<(sector_size-1))
{
unsigned long int cluster=0;
if(low==0)
cluster=((buffer[high+1] & 0x0F) <<8) | buffer[high];
else
cluster=(buffer[high+1] <<4) | ((buffer[high]&0xF0)>>4);
if((cluster!=0) && ((cluster&0x0ff8)!=(unsigned)0x0ff8) && (((cluster-i-1)*3+2)%(2*sector_size)==0))
{
unsigned int j;
for(j=0;(j<nbr_offset) &&
(info_offset[j].offset!=((cluster-i-1)*3+2)/(2*sector_size) || info_offset[j].fat_type!=12);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=((cluster-i-1)*3+2)/(2*sector_size);
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=12;
nbr_offset++;
}
}
if(low==0)
low=1;
else
{
high++;
low=0;
}
high++;
i++;
}
if((buffer[0]==0xF0 || buffer[0]>=0xF8) && buffer[1]==0xFF)
{
unsigned int j;
for(j=0;(j<nbr_offset) &&
(info_offset[j].offset!=0 || info_offset[j].fat_type!=12);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=0;
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=12;
nbr_offset++;
}
have_fat_signature=1;
}
}
if(p_fat16!=0)
{
unsigned int i,j;
const uint16_t *p16=(const uint16_t*)buffer;
unsigned int err=0;
for(i=0; (i<sector_size/2)&&(err==0); i++)
{
unsigned long int cluster=le16(p16[i]);
if(cluster==1)
{
err=1;
}
if((cluster!=0) && ((cluster&0x0fff8)!=(unsigned)0x0fff8))
{
for(j=i+1; (j<sector_size/2)&&(err==0); j++)
{
if(cluster==le16(p16[j]))
{
err=1;
}
}
}
}
if(err==0)
{
for(i=0; i<sector_size/2; i++)
{
unsigned long int cluster=le16(p16[i]);
if((cluster!=0) && ((cluster&0x0fff8)!=(unsigned)0x0fff8)&&((cluster-i-1)%(sector_size/2)==0))
{
for(j=0;(j<nbr_offset) &&
(info_offset[j].offset!=(cluster-i-1)/(sector_size/2) || info_offset[j].fat_type!=16);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=(cluster-i-1)/(sector_size/2);
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=16;
nbr_offset++;
}
}
}
}
if((buffer[0]==0xF0 || buffer[0]>=0xF8) && buffer[1]==0xFF
&& buffer[2]==0xFF && ((buffer[3] & 0xF7)==0xF7))
{
for(j=0;(j<nbr_offset)&& (info_offset[j].offset!=0 || info_offset[j].fat_type!=16);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=0;
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=16;
nbr_offset++;
}
have_fat_signature=1;
}
}
if(p_fat32!=0)
{
unsigned int i,j;
const uint32_t *p32=(const uint32_t*)buffer;
unsigned int err=0;
for(i=0; (i<sector_size/4)&&(err==0); i++)
{
unsigned long int cluster=le32(p32[i])&0x0FFFFFFF;
if(cluster==1)
{
err=1;
}
if((cluster!=0) && ((cluster&0x0ffffff8)!=(unsigned)0x0ffffff8))
{
for(j=i+1; (j<sector_size/4)&&(err==0); j++)
{
if(cluster==(le32(p32[j])&0x0FFFFFFF))
{
err=1;
}
}
}
}
if(err==0)
{
for(i=0; i<sector_size/4; i++)
{
unsigned long int cluster=le32(p32[i])&0x0FFFFFFF;
if((cluster!=0) && ((cluster&0x0ffffff8)!=(unsigned)0x0ffffff8)&&((cluster-i-1)%(sector_size/4)==0))
{
for(j=0;(j<nbr_offset) &&
((info_offset[j].offset!=(cluster-i-1)/(sector_size/4)) || (info_offset[j].fat_type!=32));j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=(cluster-i-1)/(sector_size/4);
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=32;
nbr_offset++;
}
}
}
}
if((buffer[0]==0xF0 || buffer[0]>=0xF8) && buffer[1]==0xFF &&
buffer[2]==0xFF && ((buffer[3]==0x0F) ||(buffer[3]==0xFF)) &&
buffer[4]==0xFF && buffer[5]==0xFF && buffer[6]==0xFF)
{
for(j=0;(j<nbr_offset)&&(info_offset[j].offset!=0 || info_offset[j].fat_type!=32);j++);
if(j<nbr_offset)
info_offset[j].nbr++;
else
{
info_offset[nbr_offset].offset=0;
info_offset[nbr_offset].nbr=1;
info_offset[nbr_offset].fat_type=32;
nbr_offset++;
}
have_fat_signature=1;
}
}
if(nbr_offset>0)
{
unsigned int j;
unsigned int best_j=0;
for(j=0;j<nbr_offset;j++)
{
if(info_offset[j].nbr>info_offset[best_j].nbr)
best_j=j;
}
if(info_offset[best_j].nbr>10 || have_fat_signature>0)
{
*fat_offset=info_offset[best_j].offset;
return info_offset[best_j].fat_type;
}
}
return 0;
}
static int fat_find_type(disk_t *disk_car,const partition_t *partition,const uint64_t max_offset,const int p_fat12,const int p_fat16,const int p_fat32,const int debug,const int dump_ind,const int interface,unsigned int *nbr_offset,info_offset_t *info_offset)
{
uint64_t offset;
unsigned long int old_percent=0;
unsigned char buffer[disk_car->sector_size];
int ind_stop=0;
if(debug>0)
{
log_debug("fat_find_type(max_offset=%lu, p_fat12=%d, p_fat16=%d, p_fat32=%d, debug=%d, dump_ind=%d)\n",
(long unsigned)(max_offset/disk_car->sector_size), p_fat12, p_fat16, p_fat32, debug, dump_ind);
}
if(interface)
{
wmove(stdscr,8,0);
wdoprintf(stdscr,"FAT : %s%s%s?\n",p_fat12?"12 ":"", p_fat16?"16 ":"", p_fat32?"32 ":"");
wmove(stdscr,22,0);
wattrset(stdscr, A_REVERSE);
waddstr(stdscr," Stop ");
wattroff(stdscr, A_REVERSE);
}
for(offset=disk_car->sector_size;(offset<max_offset)&&(*nbr_offset<0x200)&&!ind_stop;offset+=disk_car->sector_size)
{
unsigned long int percent=offset*100/max_offset;
if(interface && (percent!=old_percent))
{
wmove(stdscr,8,30);
wclrtoeol(stdscr); /* before addstr for BSD compatibility */
wdoprintf(stdscr,"Searching for FAT table %lu%%",percent);
old_percent=percent;
wrefresh(stdscr);
ind_stop|=check_enter_or_s(stdscr);
}
if(disk_car->read(disk_car,disk_car->sector_size, buffer, partition->part_offset+offset)==0)
{
unsigned long int fat_offset=0;
unsigned int fat_type;
fat_type=fat_find_fat_start(buffer,p_fat12,p_fat16,p_fat32,&fat_offset,disk_car->sector_size);
if(fat_type!=0 && fat_offset<=(offset/disk_car->sector_size))
{
unsigned int j;
if(debug>1)
{
log_info("fat_find_fat_start FAT%u at %lu:%lu\n", fat_type,
(long unsigned)(offset/disk_car->sector_size-fat_offset),
(long unsigned)(offset/disk_car->sector_size));
}
for(j=0;(j<*nbr_offset)&& !((info_offset[j].offset==offset/disk_car->sector_size-fat_offset)&&(info_offset[j].fat_type==fat_type));j++);
if(j<*nbr_offset)
info_offset[j].nbr++;
else
{
unsigned int new_info=0;
if(*nbr_offset<0x200)
{
new_info=*nbr_offset;
(*nbr_offset)++;
}
else
{ /* Overwrite the last information field with the lower nbr */
for(j=0;j<0x200;j++)
{
if(info_offset[j].nbr <= info_offset[new_info].nbr)
new_info=j;
}
}
info_offset[new_info].offset=offset/disk_car->sector_size-fat_offset;
info_offset[new_info].nbr=1;
info_offset[new_info].fat_type=fat_type;
}
}
}
}
if(interface)
{
wmove(stdscr,22,0);
wclrtoeol(stdscr);
wrefresh(stdscr);
}
return 0;
}
static upart_type_t fat_find_info(disk_t *disk_car,unsigned int*reserved, unsigned int*fat_length, const partition_t *partition,const uint64_t max_offset,const int p_fat12,const int p_fat16,const int p_fat32,const int debug,const int dump_ind,const int interface, const unsigned int expert, unsigned int *fats)
{
unsigned int nbr_offset=0;
unsigned int i;
info_offset_t info_offset[0x200];
upart_type_t upart_type=UP_UNK;
fat_find_type(disk_car, partition,max_offset,p_fat12,p_fat16,p_fat32,debug,dump_ind,interface,&nbr_offset,&info_offset[0]);
/*
info_offset[0].fat_type=32;
info_offset[0].offset=32;
info_offset[0].nbr=1;
info_offset[1].fat_type=32;
info_offset[1].offset=40;
info_offset[1].nbr=921;
info_offset[2].fat_type=32;
info_offset[2].offset=565;
info_offset[2].nbr=1;
info_offset[3].fat_type=32;
info_offset[3].offset=3064;
info_offset[3].nbr=921;
info_offset[4].fat_type=32;
info_offset[4].offset=3589;
info_offset[4].nbr=1;
info_offset[5].fat_type=32;
info_offset[5].offset=35190;
info_offset[5].nbr=1;
nbr_offset=6;
*/
for(i=0;i<nbr_offset;i++)
{
log_info("FAT%u at %lu(%u/%u/%u), nbr=%u\n",info_offset[i].fat_type,info_offset[i].offset,
offset2cylinder(disk_car,partition->part_offset+(uint64_t)info_offset[i].offset*disk_car->sector_size),
offset2head(disk_car,partition->part_offset+(uint64_t)info_offset[i].offset*disk_car->sector_size),
offset2sector(disk_car,partition->part_offset+(uint64_t)info_offset[i].offset*disk_car->sector_size),
info_offset[i].nbr);
if((dump_ind>0) && (interface>0))
{
unsigned char buffer[disk_car->sector_size];
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, partition->part_offset+(uint64_t)info_offset[i].offset*disk_car->sector_size)==0)
{
dump_ncurses(buffer,disk_car->sector_size);
}
}
}
if(nbr_offset==0)
{
*fat_length=0;
}
else
{
unsigned int offset_for_max_nbr=0;
unsigned int fat_found=0;
unsigned int first_fat=0;
unsigned int second_fat=0;
/* select the good type in the 3 first possibilities */
for(i=0;i<nbr_offset && (i<3 || info_offset[i].offset<=33);i++)
{
if(info_offset[i].nbr>info_offset[offset_for_max_nbr].nbr)
offset_for_max_nbr=i;
}
switch(info_offset[offset_for_max_nbr].fat_type)
{
case 12: upart_type=UP_FAT12; break;
case 16: upart_type=UP_FAT16; break;
case 32: upart_type=UP_FAT32; break;
}
for(i=0;i<nbr_offset;i++)
{
if(info_offset[i].fat_type==info_offset[offset_for_max_nbr].fat_type)
{
if(fat_found==0 && info_offset[i].nbr>=(info_offset[offset_for_max_nbr].nbr+2-1)/2)
{
first_fat=i;
fat_found++;
}
else if(fat_found==1 && info_offset[i].nbr>=(info_offset[first_fat].nbr+2-1)/2)
{
second_fat=i;
fat_found++;
}
}
}
if(fat_found==1)
{
for(i=first_fat+1;i<nbr_offset;i++)
{
if(info_offset[i].fat_type==info_offset[offset_for_max_nbr].fat_type)
{
if(fat_found==1)
{
second_fat=i;
fat_found++;
}
}
}
}
if(fat_found==1)
{
switch(upart_type)
{
case UP_FAT12:
case UP_FAT16:
*reserved=1;
if(info_offset[first_fat].offset>*reserved)
*fat_length=info_offset[first_fat].offset-*reserved;
else
*fat_length=0;
break;
case UP_FAT32:
if(info_offset[first_fat].offset==32 || info_offset[first_fat].offset==33)
*reserved=info_offset[first_fat].offset;
*fat_length=0;
break;
default:
log_critical("fat_find_info: severe error\n");
return UP_UNK;
}
}
else
{
switch(upart_type)
{
case UP_FAT12:
case UP_FAT16:
*reserved=info_offset[first_fat].offset; /* Should be 1 */
*fat_length=info_offset[second_fat].offset-*reserved;
break;
case UP_FAT32:
*reserved=info_offset[first_fat].offset;
*fat_length=info_offset[second_fat].offset-*reserved;
if(*reserved==32 || *reserved==33 || comp_FAT(disk_car,partition,*fat_length,*reserved)==0)
{
} else {
*reserved=0;
*fat_length=0;
}
break;
default:
log_critical("fat_find_info: severe error\n");
return UP_UNK;
}
}
if(debug>0)
{
log_info("first_fat %lu, second_fat %lu\n",info_offset[first_fat].offset, info_offset[second_fat].offset);
}
}
if(expert>0 && (interface>0))
{
return select_fat_info(info_offset,nbr_offset,reserved,fat_length,max_offset/disk_car->sector_size,fats);
}
return upart_type;
}
static upart_type_t select_fat_info(const info_offset_t *info_offset, const unsigned int nbr_offset,unsigned int*reserved, unsigned int*fat_length, const unsigned long int max_sector_offset, unsigned int *fats)
{
unsigned int i;
int reserved_can_be_one=0;
unsigned long int fat2_location=*reserved+*fat_length;
struct MenuItem menuSelectFAT[]=
{
{ 'P', "Previous",""},
{ 'N', "Next","" },
{ 'Q', "Proceed","Set FAT table location"},
{ 0, NULL, NULL }
};
aff_buffer(BUFFER_RESET,"Q");
aff_buffer(BUFFER_ADD,"Potential FAT location\n");
aff_buffer(BUFFER_ADD,"FAT - sector - score\n");
for(i=0;i<nbr_offset;i++)
{
aff_buffer(BUFFER_ADD," %02d %8lu %u\n",info_offset[i].fat_type,info_offset[i].offset,info_offset[i].nbr);
if(info_offset[i].fat_type<32)
{
reserved_can_be_one=1;
}
}
aff_copy(stdscr);
wmove(stdscr,4,0);
screen_buffer_display(stdscr,"",menuSelectFAT);
wmove(stdscr,INTER_FAT_ASK_Y, INTER_FAT_ASK_X);
*reserved=ask_number(*reserved,0,max_sector_offset,"FAT1 location (Number of reserved sector) ");
if(*reserved>0)
{
wmove(stdscr,INTER_FAT_ASK_Y, INTER_FAT_ASK_X);
fat2_location=ask_number(fat2_location,0,max_sector_offset,"FAT2 location ");
if(fat2_location>*reserved)
{
*fat_length=fat2_location-*reserved;
wmove(stdscr,INTER_FAT_ASK_Y, INTER_FAT_ASK_X);
*fats=ask_number(*fats,1,2,"Number of FATS (Usually 2) ");
}
else
{
*fat_length=0;
}
}
else
{
*fat_length=0;
}
for(i=0;i<nbr_offset;i++)
{
if(info_offset[i].offset==fat2_location)
{
switch(info_offset[i].fat_type)
{
case 12: return UP_FAT12;
case 16: return UP_FAT16;
case 32: return UP_FAT32;
}
}
}
for(i=0;i<nbr_offset;i++)
{
if(info_offset[i].offset==*reserved)
{
switch(info_offset[i].fat_type)
{
case 12: return UP_FAT12;
case 16: return UP_FAT16;
case 32: return UP_FAT32;
}
}
}
*reserved=0;
*fat_length=0;
return 0;
}
/* Using a couple of inodes of "." directory entries, get the cluster size and where the first cluster begins.
* */
static int find_cluster_size(disk_t *disk_car, partition_t *partition, const int debug, const int dump_ind,const int interface, unsigned int *cluster_size, unsigned long int *offset_org)
{
uint64_t offset;
uint64_t skip_offset;
unsigned char buffer[disk_car->sector_size];
int ind_stop=FALSE;
unsigned int nbr_subdir=0;
sector_cluster_t sector_cluster[10];
if(interface)
{
wmove(stdscr,22,0);
wattrset(stdscr, A_REVERSE);
waddstr(stdscr," Stop ");
wattroff(stdscr, A_REVERSE);
}
/* 2 fats, maximum cluster size=128 */
skip_offset=(uint64_t)((partition->part_size-32*disk_car->sector_size)/disk_car->sector_size/128*1.5/disk_car->sector_size*2)*disk_car->sector_size;
if(debug>0)
{
log_debug("find_cluster_size skip_sectors=%lu (skip_offset=%lu)\n",
(unsigned long)(skip_offset/disk_car->sector_size),
(unsigned long)skip_offset);
}
for(offset=skip_offset;(offset<partition->part_size)&&!ind_stop&&(nbr_subdir<10);offset+=disk_car->sector_size)
{
if(interface>0 && ((offset&(1024*disk_car->sector_size-1))==0))
{
wmove(stdscr,9,0);
wclrtoeol(stdscr);
wdoprintf(stdscr,"Search subdirectory %10lu/%lu %u",(unsigned long)(offset/disk_car->sector_size),(unsigned long)(partition->part_size/disk_car->sector_size),nbr_subdir);
wrefresh(stdscr);
ind_stop|=check_enter_or_s(stdscr);
}
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, partition->part_offset+offset)==0)
{
if(memcmp(&buffer[0],". ",8+3)==0 && memcmp(&buffer[0x20],".. ",8+3)==0)
{
unsigned long int cluster=(buffer[0*0x20+0x15]<<24) + (buffer[0*0x20+0x14]<<16) +
(buffer[0*0x20+0x1B]<<8) + buffer[0*0x20+0x1A];
log_info("sector %lu, cluster %lu\n",
(unsigned long)(offset/disk_car->sector_size), cluster);
sector_cluster[nbr_subdir].cluster=cluster;
sector_cluster[nbr_subdir].sector=offset/disk_car->sector_size;
nbr_subdir++;
if(dump_ind>0)
dump_ncurses(buffer,disk_car->sector_size);
}
}
}
return find_cluster_size_aux(sector_cluster,nbr_subdir,cluster_size,offset_org,debug,partition->part_size/disk_car->sector_size);
}
static int find_cluster_size_aux(const sector_cluster_t *sector_cluster, const unsigned int nbr_sector_cluster,unsigned int *cluster_size, unsigned long int *offset, const int debug, const unsigned long int part_size_in_sectors)
{
if(nbr_sector_cluster<2)
return 0;
{
unsigned int i,j;
unsigned int nbr_sol=0;
cluster_offset_t cluster_offset[nbr_sector_cluster*nbr_sector_cluster];
for(i=0;i<nbr_sector_cluster-1;i++)
{
for(j=i+1;j<nbr_sector_cluster;j++)
{
if(sector_cluster[j].cluster>sector_cluster[i].cluster)
{
unsigned int cluster_size_tmp=(sector_cluster[j].sector-sector_cluster[i].sector)/(sector_cluster[j].cluster-sector_cluster[i].cluster);
switch(cluster_size_tmp)
{
case 1:
case 2:
case 4:
case 8:
case 16:
case 32:
case 64:
case 128:
if(sector_cluster[i].sector>(sector_cluster[i].cluster-2)*(*cluster_size))
{
unsigned int sol_cur;
unsigned int found=0;
unsigned int offset_tmp=sector_cluster[i].sector-(sector_cluster[i].cluster-2)*cluster_size_tmp;
for(sol_cur=0;(sol_cur<nbr_sol)&&!found;sol_cur++)
{
if(cluster_offset[sol_cur].cluster_size==cluster_size_tmp &&
cluster_offset[sol_cur].offset==offset_tmp)
{
if(cluster_offset[sol_cur].first_sol==i)
{
cluster_offset[sol_cur].nbr++;
}
/* log_trace("cluster_size=%u offset=%lu nbr=%u\n",cluster_offset[sol_cur].cluster_size,cluster_offset[sol_cur].offset,cluster_offset[sol_cur].nbr); */
found=1;
}
}
if(!found)
{
cluster_offset[nbr_sol].cluster_size=cluster_size_tmp;
cluster_offset[nbr_sol].offset=offset_tmp;
cluster_offset[nbr_sol].nbr=1;
cluster_offset[nbr_sol].first_sol=i;
nbr_sol++;
}
}
break;
}
}
}
}
/* Show results */
{
unsigned int nbr_max=0;
unsigned int sol_cur;
for(sol_cur=0;sol_cur<nbr_sol;sol_cur++)
{
if(debug>0)
{
log_debug("cluster_size=%u offset=%lu nbr=%u ",cluster_offset[sol_cur].cluster_size,cluster_offset[sol_cur].offset,cluster_offset[sol_cur].nbr);
switch(no_of_cluster2part_type((part_size_in_sectors-cluster_offset[sol_cur].offset)/cluster_offset[sol_cur].cluster_size))
{
case UP_FAT12:
log_info("FAT : 12\n");
break;
case UP_FAT16:
log_info("FAT : 16\n");
break;
case UP_FAT32:
log_info("FAT : 32\n");
break;
default: /* No compiler warning */
break;
}
}
if(cluster_offset[sol_cur].nbr>nbr_max)
{
nbr_max=cluster_offset[sol_cur].nbr;
*cluster_size=cluster_offset[sol_cur].cluster_size;
*offset=cluster_offset[sol_cur].offset;
}
}
if(nbr_max>0)
{
log_info("Selected: cluster_size=%u offset=%lu nbr=%u\n",*cluster_size, *offset,nbr_max);
return 1;
}
}
}
/* Failed */
return 0;
}
static upart_type_t no_of_cluster2part_type(const unsigned long int no_of_cluster)
{
if(no_of_cluster<65525)
{
if(no_of_cluster<4085)
return UP_FAT12;
else
return UP_FAT16;
}
return UP_FAT32;
}
int rebuild_FAT_BS(disk_t *disk_car, partition_t *partition, const int debug, const int dump_ind,const int interface,const unsigned int expert, char**current_cmd)
{
unsigned long int data_size;
unsigned long int max_offset;
unsigned int fat_length=0;
unsigned int cluster_size_min=disk_car->sector_size;
unsigned int cluster_size=0;
unsigned int reserved=0;
unsigned int dir_entries=0;
unsigned int fats=2;
int p_fat12,p_fat16,p_fat32;
upart_type_t upart_type;
/*
* Using partition size, check if partition can be FAT12, FAT16 or FAT32
* */
if(partition->part_size>(uint64_t)(2*1024+1)*1024*1024)
{
p_fat32=1;
p_fat16=0;
p_fat12=0;
}
else
/* 1<<12 clusters * 8 secteurs/clusters= 32768 secteurs
fat_length=((1<<12+1)*1.5/DEFAULT_SECTOR_SIZE)+1=13; */
if(partition->part_size>=(uint64_t)(1+2*13+32768+63)*512)
{
p_fat32=1;
p_fat16=1;
p_fat12=0;
}
else
{
p_fat32=0;
p_fat16=1;
p_fat12=1;
}
#ifdef TESTING
p_fat32=1; p_fat16=1; p_fat12=1;
#endif
if(debug)
{
log_info("\n");
log_partition(disk_car,partition);
log_info("rebuild_FAT_BS p_fat12 %d, p_fat16 %d, p_fat32 %d\n", p_fat12,p_fat16,p_fat32);
}
{
/* Set fat_length_max */
unsigned long int fat_length_max;
if(p_fat32)
{ /* Cluster 512 bytes */
fat_length_max=partition->part_size/cluster_size_min*4;
}
else
if(p_fat16)
{
while(partition->part_size/cluster_size_min > (1<<16))
cluster_size_min*=2;
fat_length_max=partition->part_size/cluster_size_min*2;
}
else
{
while(partition->part_size/cluster_size_min > (1<<12))
cluster_size_min*=2;
fat_length_max=partition->part_size/cluster_size_min*1.5;
}
fat_length_max=fat_length_max/disk_car->sector_size*disk_car->sector_size;
if(debug>1)
{
log_debug("cluster_size_min %u sectors\n",cluster_size_min/disk_car->sector_size);
log_debug("fat_length_max %ld sectors\n", fat_length_max/disk_car->sector_size);
}
max_offset=fat_length_max+64*disk_car->sector_size;
}
/*
if(debug>1)
log_trace("search_fat16(partition,max_offset=%d,p_fat12=%d,p_fat16=%d,p_fat32=%d,debug=%d,dump_ind=%d)\n",max_offset,p_fat12,p_fat16,p_fat32,debug,dump_ind);
*/
if(interface)
{
aff_copy(stdscr);
wmove(stdscr,4,0);
wdoprintf(stdscr,"%s",disk_car->description(disk_car));
mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG);
wmove(stdscr,6,0);
aff_part(stdscr,AFF_PART_ORDER,disk_car,partition);
wrefresh(stdscr);
}
upart_type=fat_find_info(disk_car,&reserved, &fat_length, partition,max_offset,p_fat12,p_fat16,p_fat32,debug,dump_ind,interface,expert,&fats);
if(interface)
{
aff_copy(stdscr);
wmove(stdscr,4,0);
wdoprintf(stdscr,"%s",disk_car->description(disk_car));
mvwaddstr(stdscr,5,0,msg_PART_HEADER_LONG);
wmove(stdscr,6,0);
aff_part(stdscr,AFF_PART_ORDER,disk_car,partition);
wmove(stdscr,8,0);
wclrtoeol(stdscr);
switch(upart_type)
{
case UP_FAT12:
waddstr(stdscr,"FAT : 12");
break;
case UP_FAT16:
waddstr(stdscr,"FAT : 16");
break;
case UP_FAT32:
waddstr(stdscr,"FAT : 32");
break;
default:
waddstr(stdscr,"No FAT found");
break;
}
}
if(debug>0)
{
switch(upart_type)
{
case UP_FAT12:
log_info("FAT : 12");
break;
case UP_FAT16:
log_info("FAT : 16");
break;
case UP_FAT32:
log_info("FAT : 32");
break;
default:
log_info("No FAT found");
break;
}
log_info(", reserved=%u, fat_length=%u\n",reserved,fat_length);
}
if((upart_type!=UP_FAT12 && upart_type!=UP_FAT16 && upart_type!=UP_FAT32)||
(fat_length==0)||(reserved==0))
{
unsigned long int start_data=0;
if(find_cluster_size(disk_car, partition, debug, dump_ind, interface,&cluster_size,&start_data)==0)
{
display_message("Can't find cluster size\n");
return 0;
}
if((cluster_size<=0) || (partition->part_size/disk_car->sector_size<=start_data))
{
display_message("Can't find cluster size\n");
return 0;
}
upart_type=no_of_cluster2part_type((partition->part_size/disk_car->sector_size-start_data)/cluster_size);
switch(upart_type)
{
case UP_FAT12:
log_info("FAT : 12\n");
break;
case UP_FAT16:
log_info("FAT : 16\n");
break;
case UP_FAT32:
log_info("FAT : 32\n");
break;
default: /* No compiler warning */
break;
}
switch(upart_type)
{
case UP_FAT12:
case UP_FAT16:
reserved=1; /* must be 1 */
dir_entries=find_dir_entries(disk_car,partition,start_data-1,debug);
switch(dir_entries)
{
case 0:
log_warning("dir_entries not found, should be 512\n");
dir_entries=512;
break;
case 512:
if(debug)
log_info("dir_entries: %u\n", dir_entries);
break;
default:
log_warning("dir_entries: %u (unusual value)\n", dir_entries);
break;
}
fat_length=(start_data-reserved-((dir_entries-1)/16+1))/fats;
break;
case UP_FAT32:
reserved=32;
if((start_data&1)!=0)
reserved+=1;
fat_length=(start_data-reserved)/fats;
break;
default: /* No compiler warning */
break;
}
if(debug>0)
log_info("fat_length %u\n",fat_length);
}
if(interface)
{
if(fat_length==0)
waddstr(stdscr," Can't find FAT length\n");
wrefresh(stdscr);
}
if(upart_type && (fat_length>1))
{
/* Initialized by fat32_free_info */
unsigned int free_cluster_count=0;
unsigned int first_free_cluster=0;
/* Initialized by fat32_find_root_cluster */
unsigned long int root_cluster=0;
unsigned long int start_data=reserved+fats*fat_length;
/* FAT1x: Find size of root directory */
if((upart_type==UP_FAT12) || (upart_type==UP_FAT16))
{
int old_dir_entries=dir_entries;
dir_entries=analyse_dir_entries(disk_car,partition,start_data,debug);
log_info("dir_entries %u\n",dir_entries);
dir_entries=analyse_dir_entries2(disk_car,partition,reserved,fat_length,debug,dir_entries,upart_type,fats);
log_info("dir_entries %u\n",dir_entries);
if(dir_entries==0)
{
if(old_dir_entries>0)
fat_length=0;
/*
else
{
dir_entries=512;
log_trace("analyse_dir_entries: use default dir_entries %u\n",dir_entries);
}
*/
}
start_data+=(dir_entries+(disk_car->sector_size/32)-1)/(disk_car->sector_size/32);
}
if(partition->part_size/disk_car->sector_size<=start_data)
{
log_error("Error part_size=%lu <= start_data=%lu\n",
(unsigned long)(partition->part_size/disk_car->sector_size), start_data);
return 0;
}
data_size=partition->part_size/disk_car->sector_size-start_data;
/* Get Cluster Size */
{
int old_cluster_size=cluster_size;
cluster_size=calcul_cluster_size(upart_type,data_size,fat_length,disk_car->sector_size);
if(debug>0)
log_info("cluster_size %u\n",cluster_size);
if((cluster_size<=0)||(cluster_size>128))
{
if(old_cluster_size>0)
{
cluster_size=old_cluster_size;
log_info("Assumes previous cluster size was good\n");
}
else
{
cluster_size=0;
}
}
if(expert>0)
{
wmove(stdscr, INTER_FAT_ASK_Y, INTER_FAT_ASK_X);
cluster_size=ask_number(cluster_size,0,128,"cluster size ");
switch(cluster_size)
{
case 1:
case 2:
case 4:
case 8:
case 16:
case 32:
case 64:
case 128:
break;
default:
cluster_size=0;
break;
}
}
if(cluster_size==0)
{
display_message("Can't get cluster size\n");
return 0;
}
}
if(upart_type==UP_FAT32)
{
/* Use first fat */
fat32_free_info(disk_car,partition,reserved,data_size/cluster_size,&first_free_cluster,&free_cluster_count);
/* FAT32 : Find root cluster */
root_cluster=fat32_find_root_cluster(disk_car,partition,cluster_size,data_size/cluster_size,reserved,fat_length,interface,debug,expert,first_free_cluster,fats);
if(expert>0)
{
wmove(stdscr, INTER_FAT_ASK_Y, INTER_FAT_ASK_X);
root_cluster=ask_number(root_cluster,2,data_size/cluster_size+1,"root cluster ");
if(debug>1)
{
log_debug("root_cluster=%lu (new)\n",root_cluster);
}
}
}
if(interface)
{
wmove(stdscr,9,0);
wclrtoeol(stdscr);
wrefresh(stdscr);
}
create_fat_boot_sector(disk_car,partition, reserved, debug,dir_entries,root_cluster,cluster_size,fat_length,interface,upart_type,fats,current_cmd);
if(debug)
{
log_info("\n");
log_partition(disk_car,partition);
}
}
return 0;
}
int FAT_init_rootdir(disk_t *disk_car, partition_t *partition, const int debug)
{
if(partition->upart_type!=UP_FAT12 && partition->upart_type!=UP_FAT16)
return 1;
if(check_FAT(disk_car,partition,debug)==0)
{
unsigned char buffer[disk_car->sector_size];
unsigned long int start_rootdir,start_data,fat_length,sector;
unsigned int error=0;
struct fat_boot_sector *fat_header=(struct fat_boot_sector *)&buffer;
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, partition->part_offset)!=0)
{
display_message("FAT_init_rootdir: Can't read boot sector\n");
return 1;
}
fat_length=le16(fat_header->fat_length)>0?le16(fat_header->fat_length):le32(fat_header->fat32_length);
start_rootdir=le16(fat_header->reserved)+ fat_header->fats*fat_length;
start_data=start_rootdir+(get_dir_entries(fat_header)*32+disk_car->sector_size-1)/disk_car->sector_size;
for(sector=start_rootdir;error==0 && sector<start_data;sector++)
{
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, partition->part_offset+(uint64_t)sector*disk_car->sector_size)!=0)
{
log_error("FAT_init_rootdir: read error at sector %lu\n", sector);
}
else
{
unsigned int i;
for(i=0;error==0 && (i<disk_car->sector_size/0x20);i++)
{
if(check_entree(&buffer[i*0x20])==2)
{
error=1;
}
}
}
}
if(error==0)
{
display_message("TestDisk doesn't seem needed to reset the root directory.\n");
}
else
{
if(ask_confirmation("Initialize FAT root directory, confirm ? (Y/N)")!=0)
{
log_info("Initialize FAT root directory\n");
memset(buffer,0,disk_car->sector_size);
for(sector=start_rootdir;sector<start_data;sector++)
{
if(disk_car->write(disk_car,disk_car->sector_size, &buffer,
partition->part_offset+(uint64_t)sector*disk_car->sector_size)!=0)
{
display_message("FAT_init_rootdir: write failed.\n");
}
}
}
}
}
else
{
display_message("Boot sector not valid, can't check FAT.\n");
}
return 1;
}
enum fat_status_type { FAT_UNREADABLE=0, FAT_CORRUPTED=1, FAT_OK=2 };
enum fat_ask_repair { FAT_REPAIR_ASK=0, FAT_REPAIR_YES=1, FAT_REPAIR_NO=2 };
int repair_FAT_table(disk_t *disk_car, partition_t *partition, const int debug)
{
if(check_FAT(disk_car,partition,debug)!=0)
{
display_message("Boot sector not valid, can't check FAT.\n");
return 1;
}
{
unsigned long int start_fat1,no_of_cluster,fat_length;
unsigned int fats;
unsigned int fat32_root_cluster=0;
int fat_damaged=0;
WINDOW *window=newwin(0,0,0,0); /* full screen */
aff_copy(window);
{
unsigned char buffer[disk_car->sector_size];
struct fat_boot_sector *fat_header=(struct fat_boot_sector *)&buffer;
unsigned long int part_size,start_data;
if(disk_car->read(disk_car,disk_car->sector_size, &buffer, partition->part_offset)!=0)
{
display_message("repair_FAT_table: Can't read boot sector\n");
return 1;
}
fat_length=le16(fat_header->fat_length)>0?le16(fat_header->fat_length):le32(fat_header->fat32_length);
part_size=(sectors(fat_header)>0?sectors(fat_header):fat_header->total_sect);
start_fat1=le16(fat_header->reserved);
fats=fat_header->fats;
start_data=start_fat1+fats*fat_length+(get_dir_entries(fat_header)*32+disk_car->sector_size-1)/disk_car->sector_size;
no_of_cluster=(part_size-start_data)/fat_header->cluster_size;
fat32_root_cluster=le32(fat_header->root_cluster);
log_info("repair_FAT_table cluster=2..%lu\n",no_of_cluster+1);
}
{
unsigned char buffer_fat[fats][2*disk_car->sector_size];
unsigned int fat_status[fats];
unsigned int allow_write[fats];
unsigned int fat_history[fats][3];
unsigned int old_offset_s=1234;
unsigned int fat_mismatch=0;
unsigned int fat_nbr;
unsigned long int cluster;
unsigned long int old_percent=0;
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
fat_history[fat_nbr][FAT_UNREADABLE]=0;
fat_history[fat_nbr][FAT_CORRUPTED]=0;
fat_history[fat_nbr][FAT_OK]=0;
allow_write[fat_nbr]=FAT_REPAIR_ASK;
fat_status[fat_nbr]=FAT_OK;
}
for(cluster=2;cluster<=no_of_cluster+1;cluster++)
{
unsigned long int next_cluster;
unsigned int offset_s,offset_o;
if(partition->upart_type==UP_FAT32)
{
offset_s=cluster/(disk_car->sector_size/4);
offset_o=cluster%(disk_car->sector_size/4);
}
else if(partition->upart_type==UP_FAT16)
{
offset_s=cluster/(disk_car->sector_size/2);
offset_o=cluster%(disk_car->sector_size/2);
}
else
{
offset_s=(cluster+cluster/2)/disk_car->sector_size;
offset_o=(cluster+cluster/2)%disk_car->sector_size;
}
if(offset_s!=old_offset_s)
{
unsigned long int percent=cluster*100/(no_of_cluster+1);
if(percent!=old_percent)
{
wmove(window,4,0);
wdoprintf(window,"Checking FAT %lu%%",percent);
wrefresh(window);
old_percent=percent;
}
/* Write if necessary */
{
unsigned int nbr_fat_unreadable=0;
unsigned int nbr_fat_corrupted=0;
unsigned int nbr_fat_ok=0;
unsigned int good_fat_nbr=0;
/* Some stats about FAT table */
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
switch(fat_status[fat_nbr])
{
case FAT_UNREADABLE:
nbr_fat_unreadable++;
fat_history[fat_nbr][FAT_UNREADABLE]++;
break;
case FAT_CORRUPTED:
nbr_fat_corrupted++;
fat_history[fat_nbr][FAT_CORRUPTED]++;
break;
case FAT_OK:
nbr_fat_ok++;
good_fat_nbr=fat_nbr;
fat_history[fat_nbr][FAT_OK]++;
break;
}
}
if(fat_mismatch!=0)
{
if(nbr_fat_ok>1)
{
good_fat_nbr=0;
for(fat_nbr=1;fat_nbr<fats;fat_nbr++)
{
if(fat_history[fat_nbr][FAT_OK]>fat_history[good_fat_nbr][FAT_OK])
{
good_fat_nbr=fat_nbr;
}
else if(fat_history[fat_nbr][FAT_OK] == fat_history[good_fat_nbr][FAT_OK])
{
unsigned long int fat_offset=0;
if(fat_find_fat_start(buffer_fat[fat_nbr], (partition->upart_type==UP_FAT12),
(partition->upart_type==UP_FAT16), (partition->upart_type==UP_FAT32),
&fat_offset,disk_car->sector_size)!=0)
good_fat_nbr=fat_nbr;
}
}
}
}
if(debug>1 || nbr_fat_ok!=fats || fat_mismatch>0)
{
log_debug("nbr_fat_unreadable %u, nbr_fat_corrupted %u, nbr_fat_ok %u, good_fat_nbr %u, fat_mismatch %u\n",
nbr_fat_unreadable, nbr_fat_corrupted, nbr_fat_ok, good_fat_nbr, fat_mismatch);
}
/* Write FAT if necessary */
if(fat_mismatch!=0)
{
fat_damaged=1;
if(nbr_fat_ok>=1)
{
/* Use the good/best FAT to repair the bad one */
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(fat_nbr!=good_fat_nbr)
{
if(debug>2)
{
dump_log(buffer_fat[fat_nbr],disk_car->sector_size);
}
if(allow_write[fat_nbr]==FAT_REPAIR_ASK)
{
if(ask_confirmation("Use FAT%u to repair FAT%u table, confirm ? (Y/N)",good_fat_nbr+1,fat_nbr+1)!=0)
{
allow_write[fat_nbr]=FAT_REPAIR_YES;
}
else
{
allow_write[fat_nbr]=FAT_REPAIR_NO;
log_info("repair_FAT_table: doesn't correct FAT%u (sector %lu) using FAT%u\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s, good_fat_nbr+1);
}
}
if(allow_write[fat_nbr]==FAT_REPAIR_YES)
{
log_info("repair_FAT_table: correcting FAT%u (sector %lu) using FAT%u\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s, good_fat_nbr+1);
if(disk_car->write(disk_car,disk_car->sector_size, &buffer_fat[good_fat_nbr],
partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+old_offset_s)*disk_car->sector_size)!=0)
{
display_message("repair_FAT_table: write failed.\n");
}
}
}
}
}
else
{
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(allow_write[fat_nbr]==FAT_REPAIR_ASK)
{
if(ask_confirmation("Remove invalid cluster from FAT%u table, confirm ? (Y/N)",fat_nbr+1)!=0)
{
allow_write[fat_nbr]=FAT_REPAIR_YES;
}
else
{
allow_write[fat_nbr]=FAT_REPAIR_NO;
log_info("repair_FAT_table: doesn't correct FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
}
if(allow_write[fat_nbr]==FAT_REPAIR_YES)
{
log_info("repair_FAT_table: correcting FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
if(disk_car->write(disk_car,disk_car->sector_size, &buffer_fat[fat_nbr],
partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+old_offset_s)*disk_car->sector_size)!=0)
{
display_message("repair_FAT_table: write failed.\n");
}
}
}
}
}
else
{
/* only one fat or fat match */
if(nbr_fat_ok==0)
{ /* fat_corrupted */
fat_damaged=1;
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(debug>2)
{
dump_log(buffer_fat[fat_nbr],disk_car->sector_size);
}
if(allow_write[fat_nbr]==FAT_REPAIR_ASK)
{
if(ask_confirmation("Remove invalid cluster from FAT%u table, confirm ? (Y/N)",fat_nbr+1)!=0)
{
allow_write[fat_nbr]=FAT_REPAIR_YES;
log_info("repair_FAT_table: correcting FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
else
{
allow_write[fat_nbr]=FAT_REPAIR_NO;
log_info("repair_FAT_table: doesn't correct FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
}
if(allow_write[fat_nbr]==FAT_REPAIR_YES)
{
if(disk_car->write(disk_car,disk_car->sector_size, &buffer_fat[fat_nbr],
partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+old_offset_s)*disk_car->sector_size)!=0)
{
display_message("repair_FAT_table: write failed.\n");
}
}
}
}
}
}
/* Read FAT */
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
fat_status[fat_nbr]=FAT_OK;
if(debug>1)
{
log_info("repair_FAT_table: read sector %lu (FAT%u)\n",(start_fat1+fat_length*fat_nbr+offset_s),fat_nbr+1);
}
if(disk_car->read(disk_car,(partition->upart_type==UP_FAT12?2*disk_car->sector_size:disk_car->sector_size),
&buffer_fat[fat_nbr], partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+offset_s)*disk_car->sector_size)!=0)
{
log_error("repair_FAT_table: read error sector %lu\n",(start_fat1+fat_length*fat_nbr+offset_s));
memset(&buffer_fat[fat_nbr],0,2*disk_car->sector_size);
fat_status[fat_nbr]=FAT_UNREADABLE;
}
if(debug>1)
{
dump_log(buffer_fat[fat_nbr],disk_car->sector_size);
}
}
/* Compare FAT */
fat_mismatch=0;
for(fat_nbr=1;(fat_nbr<fats)&&(fat_mismatch==0);fat_nbr++)
{
if(memcmp(&buffer_fat[0],&buffer_fat[fat_nbr], disk_car->sector_size)!=0)
fat_mismatch=1;
}
}
/* Repair FAT if necessary */
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(partition->upart_type==UP_FAT32)
{
uint32_t *p32=(uint32_t*)&buffer_fat[fat_nbr];
next_cluster=(le32(p32[offset_o]) & 0xFFFFFFF);
if((next_cluster<0x0FFFFFF7 && (next_cluster==1 || next_cluster>no_of_cluster+1)) ||
(cluster==fat32_root_cluster && next_cluster==0))
{
#ifdef DEBUG
log_trace("FAT%u cluster %lu(%lx)->%lu(%lx)\n",fat_nbr+1,cluster,cluster,next_cluster,next_cluster);
#endif
p32[offset_o]=le32(FAT32_EOC);
fat_status[fat_nbr]=FAT_CORRUPTED;
}
}
else if(partition->upart_type==UP_FAT16)
{
uint16_t *p16=(uint16_t*)&buffer_fat[fat_nbr];
next_cluster=le16(p16[offset_o]);
if(next_cluster<0xFFF7 && (next_cluster==1 || next_cluster>no_of_cluster+1))
{
p16[offset_o]=le16(FAT16_EOC);
fat_status[fat_nbr]=FAT_CORRUPTED;
}
}
else
{
if((cluster&1)!=0)
next_cluster=le16((*((uint16_t*)&buffer_fat[fat_nbr][offset_o])))>>4;
else
next_cluster=le16(*((uint16_t*)&buffer_fat[fat_nbr][offset_o]))&0x0FFF;
if(next_cluster<0x0FF7 && (next_cluster==1 || next_cluster>no_of_cluster+1))
{
if((cluster&1)!=0)
*((uint16_t*)&buffer_fat[fat_nbr][offset_o])=le16((FAT12_EOC<<4)|(le16((*((uint16_t*)&buffer_fat[fat_nbr][offset_o])))&0x0F));
else
*((uint16_t*)&buffer_fat[fat_nbr][offset_o])=le16(FAT12_EOC |(le16((*((uint16_t*)&buffer_fat[fat_nbr][offset_o])))&0xF000));
fat_status[fat_nbr]=FAT_CORRUPTED;
}
}
}
old_offset_s=offset_s;
}
/* Write if necessary the last cluster */
{
unsigned int nbr_fat_unreadable=0;
unsigned int nbr_fat_corrupted=0;
unsigned int nbr_fat_ok=0;
unsigned int good_fat_nbr=0;
/* Some stats about FAT table */
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
switch(fat_status[fat_nbr])
{
case FAT_UNREADABLE:
nbr_fat_unreadable++;
fat_history[fat_nbr][FAT_UNREADABLE]++;
break;
case FAT_CORRUPTED:
nbr_fat_corrupted++;
fat_history[fat_nbr][FAT_CORRUPTED]++;
break;
case FAT_OK:
nbr_fat_ok++;
good_fat_nbr=fat_nbr;
fat_history[fat_nbr][FAT_OK]++;
break;
}
}
if(fat_mismatch!=0)
{
if(nbr_fat_ok>1)
{
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(fat_nbr!=good_fat_nbr)
{
if(fat_history[fat_nbr][FAT_OK]>fat_history[good_fat_nbr][FAT_OK])
{
good_fat_nbr=fat_nbr;
}
else if(fat_history[fat_nbr][FAT_OK] == fat_history[good_fat_nbr][FAT_OK])
{
unsigned long int fat_offset=0;
if(fat_find_fat_start(buffer_fat[fat_nbr], (partition->upart_type==UP_FAT12),
(partition->upart_type==UP_FAT16), (partition->upart_type==UP_FAT32),
&fat_offset,disk_car->sector_size)!=0)
good_fat_nbr=fat_nbr;
}
}
}
}
}
if(debug>1 || nbr_fat_ok!=fats || fat_mismatch>0)
{
log_info("nbr_fat_unreadable %u, nbr_fat_corrupted %u, nbr_fat_ok %u, good_fat_nbr %u, fat_mismatch %u\n",
nbr_fat_unreadable, nbr_fat_corrupted, nbr_fat_ok, good_fat_nbr, fat_mismatch);
}
/* Write FAT if necessary */
if(fat_mismatch!=0)
{
fat_damaged=1;
if(nbr_fat_ok>=1)
{
/* Use the good/best FAT to repair the bad one */
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(fat_nbr!=good_fat_nbr)
{
if(debug>2)
{
dump_log(buffer_fat[fat_nbr],disk_car->sector_size);
}
if(allow_write[fat_nbr]==FAT_REPAIR_ASK)
{
if(ask_confirmation("Use FAT%u to repair FAT%u table, confirm ? (Y/N)",good_fat_nbr+1,fat_nbr+1)!=0)
{
allow_write[fat_nbr]=FAT_REPAIR_YES;
log_info("repair_FAT_table: correcting FAT%u (sector %lu) using FAT%u\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s, good_fat_nbr+1);
}
else
{
allow_write[fat_nbr]=FAT_REPAIR_NO;
log_info("repair_FAT_table: doesn't correct FAT%u (sector %lu) using FAT%u\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s, good_fat_nbr+1);
}
}
if(allow_write[fat_nbr]==FAT_REPAIR_YES)
{
if(disk_car->write(disk_car,disk_car->sector_size, &buffer_fat[good_fat_nbr],
partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+old_offset_s)*disk_car->sector_size)!=0)
{
display_message("repair_FAT_table: write failed.\n");
}
}
}
}
}
else
{
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(allow_write[fat_nbr]==FAT_REPAIR_ASK)
{
if(ask_confirmation("Remove invalid cluster from FAT%u table, confirm ? (Y/N)",fat_nbr+1)!=0)
{
allow_write[fat_nbr]=FAT_REPAIR_YES;
log_info("repair_FAT_table: correcting FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
else
{
allow_write[fat_nbr]=FAT_REPAIR_NO;
log_info("repair_FAT_table: doesn't correct FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
}
if(allow_write[fat_nbr]==FAT_REPAIR_YES)
{
if(disk_car->write(disk_car,disk_car->sector_size, &buffer_fat[fat_nbr],
partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+old_offset_s)*disk_car->sector_size)!=0)
{
display_message("repair_FAT_table: write failed.\n");
}
}
}
}
}
else
{
/* only one fat or fat match */
if(nbr_fat_ok==0)
{ /* fat_corrupted */
fat_damaged=1;
for(fat_nbr=0;fat_nbr<fats;fat_nbr++)
{
if(debug>2)
{
dump_log(buffer_fat[fat_nbr],disk_car->sector_size);
}
if(allow_write[fat_nbr]==FAT_REPAIR_ASK)
{
if(ask_confirmation("Remove invalid cluster from FAT%u table, confirm ? (Y/N)",fat_nbr+1)!=0)
{
allow_write[fat_nbr]=FAT_REPAIR_YES;
log_info("repair_FAT_table: correcting FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
else
{
allow_write[fat_nbr]=FAT_REPAIR_NO;
log_info("repair_FAT_table: doesn't correct FAT%u (sector %lu)\n",fat_nbr+1,
start_fat1+fat_length*fat_nbr+old_offset_s);
}
}
if(allow_write[fat_nbr]==FAT_REPAIR_YES)
{
if(disk_car->write(disk_car,disk_car->sector_size, &buffer_fat[fat_nbr],
partition->part_offset+(uint64_t)(start_fat1+fat_length*fat_nbr+old_offset_s)*disk_car->sector_size)!=0)
{
display_message("repair_FAT_table: write failed.\n");
}
}
}
}
}
}
}
if(fat_damaged==0)
{
display_message("FATs seems Ok, nothing to do.\n");
}
delwin(window);
(void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
touchwin(stdscr);
#endif
}
return 0;
}
static int write_FAT_boot_code_aux(unsigned char *buffer)
{
const unsigned char boot_code[DEFAULT_SECTOR_SIZE]= {
0xeb, 0x3c, 0x90, 0x6d, 0x6b, 0x64, 0x6f, 0x73, 0x66, 0x73, 0x00, 0x00, 0x02, 0x08, 0x01, 0x00,
0x02, 0x00, 0x02, 0x00, 0x00, 0xf8, 0xcc, 0x00, 0x3f, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x5a, 0x5f, 0x06, 0x00, 0x00, 0x00, 0x29, 0xf8, 0x3f, 0x7c, 0x3e, 'T', 'E', 'S', 'T', 'D',
'I', 'S', 'K', 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20, 0x0e, 0x1f,
0xbe, 0x5b, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0x0b, 0x56, 0xb4, 0x0e, 0xbb, 0x07, 0x00, 0xcd, 0x10,
0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 'T', 'h', 'i', 's', ' ',
'i', 's', ' ', 'n', 'o', 't', ' ', 'a', ' ', 'b', 'o', 'o', 't', 'a', 'b', 'l',
'e', ' ', 'd', 'i', 's', 'k', '.', ' ', ' ', 'P', 'l', 'e', 'a', 's', 'e', ' ',
'i', 'n', 's', 'e', 'r', 't', ' ', 'a', ' ', 'b', 'o', 'o', 't', 'a', 'b', 'l',
'e', ' ', 'f', 'l', 'o', 'p', 'p', 'y', ' ', 'a', 'n', 'd', 0x0d, 0x0a, 'p', 'r',
'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', ' ', 't',
'r', 'y', ' ', 'a', 'g', 'a', 'i', 'n', ' ', '.', '.', '.', ' ', 0x0d, 0x0a, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
};
memcpy(buffer,&boot_code,DEFAULT_SECTOR_SIZE);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1