void read_fat(DOS_FS *fs) { int eff_size; unsigned long i; void *first,*second,*use; int first_ok,second_ok; eff_size = ((fs->clusters+2)*fs->fat_bits+7)/8; first = alloc(eff_size); second = alloc(eff_size); fs_read(fs->fat_start,eff_size,first); fs_read(fs->fat_start+fs->fat_size,eff_size,second); use = first; if (memcmp(first,second,eff_size) != 0) { FAT_ENTRY first_media, second_media; get_fat(&first_media,first,0,fs); get_fat(&second_media,second,0,fs); first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); if (first_ok && !second_ok) { printf("FATs differ - using first FAT.\n"); fs_write(fs->fat_start+fs->fat_size,eff_size,use = first); } if (!first_ok && second_ok) { printf("FATs differ - using second FAT.\n"); fs_write(fs->fat_start,eff_size,use = second); } if (first_ok && second_ok) { if (interactive) { printf("FATs differ but appear to be intact. Use which FAT ?\n" "1) Use first FAT\n2) Use second FAT\n"); if (get_key("12","?") == '1') fs_write(fs->fat_start+fs->fat_size,eff_size,use = first); else fs_write(fs->fat_start,eff_size,use = second); } else { printf("FATs differ but appear to be intact. Using first " "FAT.\n"); fs_write(fs->fat_start+fs->fat_size,eff_size,use = first); } } if (!first_ok && !second_ok) { printf("Both FATs appear to be corrupt. Giving up.\n"); exit(1); } } fs->fat = qalloc(&mem_queue,sizeof(FAT_ENTRY)*(fs->clusters+2)); for (i = 2; i < fs->clusters+2; i++) get_fat(&fs->fat[i],use,i,fs); for (i = 2; i < fs->clusters+2; i++) if (fs->fat[i].value >= fs->clusters+2 && (fs->fat[i].value < FAT_MIN_BAD(fs))) { printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", i-2,fs->fat[i].value,fs->clusters+2-1); set_fat(fs,i,-1); } free(first); free(second); }
static int32_t inter_fs_write( uint32_t cid, uint16_t blk, uint32_t offset, uint8_t *pbuf, uint32_t size ) { int32_t rtn; uint32_t i, cnt, c_size; uint16_t a_blk, prevblk, nextblk; uint8_t *p_kbuf; if( size == 0 ) { return(DNAND_NO_ERROR); } if( cid >= DNAND_FS_CLID_NUM ) { return(DNAND_PARAM_ERROR); } if( ( (blk<DNAND_FS_BLK_DATA_ST)||(blk>=DNAND_FS_BLK_NUM) )&& (blk != DNAND_FS_BLK_UNUSE) ) { return(DNAND_MNG_ERROR); } rtn = DNAND_NO_ERROR; cnt = (offset+size+DNAND_FS_BLK_SIZE-1)/DNAND_FS_BLK_SIZE; a_blk = alloc_fat(); if( a_blk >= DNAND_FS_BLK_NUM ) { return(DNAND_NOSPC_ERROR); } prevblk = DNAND_FS_BLK_UNUSE; p_kbuf = (uint8_t*)kmalloc(DNAND_FS_BLK_SIZE, GFP_KERNEL); if( p_kbuf == NULL ) { return(DNAND_NOMEM_ERROR); } for( i=0; i<cnt; i++ ) { if( offset >= DNAND_FS_BLK_SIZE ) { if( (blk>=DNAND_FS_BLK_DATA_ST)&&(blk<DNAND_FS_BLK_NUM) ) { offset -= DNAND_FS_BLK_SIZE; prevblk = blk; blk = get_fat( prevblk ); continue; } memset(p_kbuf, 0x00, DNAND_FS_BLK_SIZE); rtn = dnand_blk_write( a_blk, 0, p_kbuf, DNAND_FS_BLK_SIZE ); offset -= DNAND_FS_BLK_SIZE; } else { if( (offset+size) >= DNAND_FS_BLK_SIZE ) { c_size = DNAND_FS_BLK_SIZE-offset; } else { c_size = size; } if( c_size < DNAND_FS_BLK_SIZE ) { if( (blk>=DNAND_FS_BLK_DATA_ST)&&(blk<DNAND_FS_BLK_NUM) ) { rtn = dnand_blk_read( blk, 0, p_kbuf, DNAND_FS_BLK_SIZE ); if( rtn == DNAND_NO_ERROR ) { memcpy( p_kbuf+offset, pbuf, c_size ); rtn = dnand_blk_write( a_blk, 0, p_kbuf, DNAND_FS_BLK_SIZE ); } } else { memset( p_kbuf, 0x00, DNAND_FS_BLK_SIZE ); memcpy( p_kbuf+offset, pbuf, c_size ); rtn = dnand_blk_write( a_blk, 0, p_kbuf, DNAND_FS_BLK_SIZE ); } } else { rtn = dnand_blk_write( a_blk, 0, pbuf, c_size ); } offset = 0; size -= c_size; pbuf += c_size; } if( rtn != DNAND_NO_ERROR ) { break; } nextblk = get_fat( blk ); blk = nextblk; if( ((nextblk >= DNAND_FS_BLK_DATA_ST)&&(nextblk < DNAND_FS_BLK_NUM)) || (nextblk == DNAND_FS_BLK_EOF) ) { set_fat( a_blk, nextblk ); } else { set_fat( a_blk, DNAND_FS_BLK_EOF ); } if( (prevblk >= DNAND_FS_BLK_DATA_ST)&&(prevblk < DNAND_FS_BLK_NUM) ) { set_fat( prevblk, a_blk ); } if( i == 0 ) { set_clid( cid, a_blk ); } prevblk = a_blk; if( (i+1) < cnt ) { a_blk = alloc_fat(); if( a_blk >= DNAND_FS_BLK_NUM ) { rtn = DNAND_NOSPC_ERROR; break; } } } kfree(p_kbuf); return(rtn); }
/** * Build a bookkeeping structure from the partition's FAT table. * If the partition has multiple FATs and they don't agree, try to pick a winner, * and queue a command to overwrite the loser. * One error that is fixed here is a cluster that links to something out of range. * * @param[inout] fs Information about the filesystem */ void read_fat(DOS_FS * fs) { int eff_size; unsigned long i; void *first, *second = NULL; int first_ok, second_ok; unsigned long total_num_clusters; /* Clean up from previous pass */ if (fs->fat) free(fs->fat); if (fs->cluster_owner) free(fs->cluster_owner); fs->fat = NULL; fs->cluster_owner = NULL; total_num_clusters = fs->clusters + 2UL; eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL; first = alloc(eff_size); fs_read(fs->fat_start, eff_size, first); if (fs->nfats > 1) { second = alloc(eff_size); fs_read(fs->fat_start + fs->fat_size, eff_size, second); } if (second && memcmp(first, second, eff_size) != 0) { FAT_ENTRY first_media, second_media; get_fat(&first_media, first, 0, fs); get_fat(&second_media, second, 0, fs); first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); if (first_ok && !second_ok) { printf("FATs differ - using first FAT.\n"); fs_write(fs->fat_start + fs->fat_size, eff_size, first); } if (!first_ok && second_ok) { printf("FATs differ - using second FAT.\n"); fs_write(fs->fat_start, eff_size, second); memcpy(first, second, eff_size); } if (first_ok && second_ok) { if (interactive) { printf("FATs differ but appear to be intact. Use which FAT ?\n" "1) Use first FAT\n2) Use second FAT\n"); if (get_key("12", "?") == '1') { fs_write(fs->fat_start + fs->fat_size, eff_size, first); } else { fs_write(fs->fat_start, eff_size, second); memcpy(first, second, eff_size); } } else { printf("FATs differ but appear to be intact. Using first " "FAT.\n"); fs_write(fs->fat_start + fs->fat_size, eff_size, first); } } if (!first_ok && !second_ok) { printf("Both FATs appear to be corrupt. Giving up.\n"); exit(1); } } if (second) { free(second); } fs->fat = (unsigned char *)first; fs->cluster_owner = alloc(total_num_clusters * sizeof(DOS_FILE *)); memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *))); /* Truncate any cluster chains that link to something out of range */ for (i = 2; i < fs->clusters + 2; i++) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); if (curEntry.value == 1) { printf("Cluster %ld out of range (1). Setting to EOF.\n", i - 2); set_fat(fs, i, -1); } if (curEntry.value >= fs->clusters + 2 && (curEntry.value < FAT_MIN_BAD(fs))) { printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", i - 2, curEntry.value, fs->clusters + 2 - 1); set_fat(fs, i, -1); } } }