/* File: fat_adv.c Copyright (C) 1998-2007 Christophe GRENIER 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 #endif #include #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_SYS_STAT_H #include #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) && (inext,i++); for(i=offset;(current_file!=NULL) &&((i-offset)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_numnext!=NULL) { pos=pos->next; pos_num++; } if(pos_num>=offset+INTER_DIR) offset++; break; case KEY_PPAGE: for(i=0;(iprev!=NULL);i++) { pos=pos->prev; pos_num--; if(pos_numnext!=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 && (isector_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)&&(iread(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>4); if((cluster!=0) && ((cluster&0x0ff8)!=(unsigned)0x0ff8) && (((cluster-i-1)*3+1)%(2*sector_size)==0)) { unsigned int j; for(j=0;(j>4); if((cluster!=0) && ((cluster&0x0ff8)!=(unsigned)0x0ff8) && (((cluster-i-1)*3+2)%(2*sector_size)==0)) { unsigned int j; for(j=0;(j=0xF8) && buffer[1]==0xFF) { unsigned int j; for(j=0;(j=0xF8) && buffer[1]==0xFF && buffer[2]==0xFF && ((buffer[3] & 0xF7)==0xF7)) { for(j=0;(j=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;(j0) { unsigned int j; unsigned int best_j=0; for(j=0;jinfo_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;(offsetsector_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;ipart_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;iinfo_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=(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*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;i0) { 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;isector_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;(offsetpart_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;isector_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_cur0) { 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 && sectorread(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 && (isector_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;sectorwrite(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_nbrupart_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_nbr1) { good_fat_nbr=0; for(fat_nbr=1;fat_nbrfat_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_nbr2) { 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_nbrwrite(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_nbr2) { 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_nbr1) { 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_nbrsector_size)!=0) fat_mismatch=1; } } /* Repair FAT if necessary */ for(fat_nbr=0;fat_nbrupart_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_nbr1) { for(fat_nbr=0;fat_nbrfat_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_nbr2) { 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_nbrwrite(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_nbr2) { 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; }