#include #include #include #include #define FILE_END 0xFFFFFFFF #define FILE_FREE 0x00000000 #define FAT_ATTR_READONLY 0x01 #define FAT_ATTR_HIDDEN 0x02 #define FAT_ATTR_SYSTEM 0x04 #define FAT_ATTR_VOLID 0x08 #define FAT_ATTR_DIR 0x10 #define FAT_ATTR_ARCHIVE 0x20 #define FAT_LFN FAT_ATTR_READONLY | FAT_ATTR_HIDDEN | FAT_ATTR_SYSTEM | FAT_ATTR_VOLID typedef uint8_t *buffer_t; typedef struct{ uint8_t jmp[3]; char oem[8]; uint16_t bytes_per_sector; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t fat_count; uint16_t root_dir_entries; uint16_t sectors_in_volume_small; uint8_t media_descriptor; uint16_t sectors_per_fat_small; uint16_t sectors_per_track; uint16_t heads_per_volume; uint32_t partition_start; uint32_t large_sector_count; uint32_t sectors_per_fat; uint16_t flags; uint16_t fat_verno; uint32_t root_dir_cluster; uint16_t fsinfo_sector; uint16_t backup_boot_sector; uint8_t reserved[12]; uint8_t drive_no; uint8_t nt_flags; uint8_t sig; uint32_t vol_uuid; char volid[11]; char sysid[8]; }__attribute__((packed))fat_bpb_t; typedef struct{ uint32_t lead_sig; uint8_t reserved[480]; uint32_t sig0; uint32_t last_free_cluster; uint32_t cluster_search_start; uint8_t reserved0[12]; uint32_t trail_sig; }fsinfo_t; typedef struct{ char filename[8]; char ext[3]; uint8_t attributes; uint8_t ntflags; uint8_t creation_time; uint16_t create_time; uint16_t create_date; uint16_t last_access; uint16_t start_cluster_high; uint16_t last_modification_time; uint16_t last_modification_date; uint16_t start_cluster_low; uint32_t size_bytes; }__attribute__((packed)) file_t; typedef struct{ fat_bpb_t bpb; fsinfo_t fsinfo; uint32_t *file_table; uint32_t file_table_size_sectors; file_t *root_dir; }fat_info; int verbose = 0; int read_sector(FILE *file, uint32_t sector_start, uint32_t count, buffer_t buf){ verbose && printf("Attempting to read bytes 0x%x - 0x%x\n", sector_start * 512, sector_start * 512 + count * 512); // rewind(file); int result = fseek(file, sector_start * 512, SEEK_SET); if(result){ return result; } fflush(stdout); memset(buf, 0, count * 512); result = fread(buf, 512, count, file); return result; } int write_sector(FILE *file, uint32_t sector_start, uint32_t count, buffer_t buf){ // printf("pre seek\n"); // rewind(file); int result = fseek(file, sector_start * 512, SEEK_SET); if(result){ printf("Error during seek\n"); return result; } result = fwrite(buf, 512, count, file); // verbose && printf("Writing sector %d. res: %d\n", sector_start, result); return result; } uint32_t find_free_cluster(fat_info *info){ // verbose && printf("finding free cluster\n"); uint32_t start = info->fsinfo.cluster_search_start; if(start < 2){ start = 2; } for(int i = start; i < (info->bpb.large_sector_count/info->bpb.sectors_per_cluster); i++){ // printf("%8x, %8x\n", i, info->file_table[i]); if(info->file_table[i]) continue; // verbose && printf("Free cluster found: %d | %d\n", i, info->file_table[i]); return i; } } void write_cluster_value(fat_info *info, uint32_t value, uint32_t cluster){ // printf("writing cluster :3\n"); for(int i = 0; i < info->bpb.fat_count; i++){ info->file_table[cluster + i * info->bpb.sectors_per_fat * (info->bpb.bytes_per_sector/4)] = value; } } //read cluster - returns next cluster in chain uint32_t read_cluster(fat_info *info, uint32_t cluster, buffer_t buffer, size_t size, FILE *disk){ // uint8_t *new_buffer = calloc(4096, 1 * (info->bpb.bytes_per_sector * info->bpb.sectors_per_cluster)); read_sector(disk, info->bpb.reserved_sectors + info->file_table_size_sectors + info->bpb.partition_start + ((cluster - 2) * info->bpb.sectors_per_cluster), info->bpb.sectors_per_cluster, buffer); // memcpy(buffer, new_buffer, size); return info->file_table[cluster]; } //write size bytes to file from buffer. no value may be zero or null. If file is null, will write to root directory int write_file(fat_info *info, file_t *file, buffer_t buffer, size_t size, FILE *disk){ uint32_t was_root = 0; if(!file){ was_root = 1; file_t *root_file = malloc(sizeof(file_t)); root_file->start_cluster_low = info->bpb.root_dir_cluster&0xffff; root_file->start_cluster_high = info->bpb.root_dir_cluster>>16; // printf("Rootdirwrite: %d", root_file.start_cluster_high << 16 | root_file.start_cluster_low); file = root_file; } uint32_t first_cluster = file->start_cluster_low | (file->start_cluster_high << 16); uint32_t clusters = (size / (info->bpb.bytes_per_sector * info->bpb.sectors_per_cluster )) + 1; uint8_t *new_buffer = calloc(1, clusters * (info->bpb.bytes_per_sector * info->bpb.sectors_per_cluster )); memcpy(new_buffer, buffer, size); uint32_t cluster = first_cluster; // printf("start: %d count: %d\n", cluster, clusters); // return -1; for(uint32_t i = 0; i < clusters; i++){ write_sector(disk, info->bpb.reserved_sectors + info->file_table_size_sectors + info->bpb.partition_start + ((cluster - 2) * info->bpb.sectors_per_cluster), info->bpb.sectors_per_cluster, new_buffer + i * info->bpb.sectors_per_cluster * info->bpb.bytes_per_sector); uint32_t next_cluster = info->file_table[cluster]; if(next_cluster == -1){ next_cluster = find_free_cluster(info); write_cluster_value(info, next_cluster, cluster); write_cluster_value(info, -1, next_cluster); } // verbose && printf("writing at cluster %-d. Next: %-d", cluster, next_cluster); cluster = next_cluster; } write_cluster_value(info, -1, cluster); if(was_root){ free(file); } free(new_buffer); return size * info->bpb.bytes_per_sector * info->bpb.sectors_per_cluster; } int create_file(char *path, fat_info *info, FILE *file, FILE *disk){ file_t *root_files = calloc(info->bpb.sectors_per_cluster * 512, sizeof(file_t)); // read_file(info, 0, (buffer_t)root_files, (uint32_t)4096, disk); read_cluster(info, info->bpb.root_dir_cluster, (buffer_t)root_files, info->bpb.sectors_per_cluster * 512, disk); uint32_t index = 0; while(root_files[index].filename[0]){ index++; } // printf("%s\n", root_files[index].filename); verbose && printf("Creating file at index: %d\n", index); char *ext = strchr(path, '.'); char *tmpptr = path; int nidx = 0; while(tmpptr < ext){ root_files[index].filename[nidx++] = *(tmpptr++); } for(int i = 1; i < 4 && ext[i]; i++){ root_files[index].ext[i-1] = ext[i]; } rewind(file); fseek(file, 0, SEEK_END); uint32_t size = ftell(file); root_files[index].size_bytes = size; uint32_t first_cluster = find_free_cluster(info); info->file_table[first_cluster] = -1; root_files[index].start_cluster_low = first_cluster & 0xffff; root_files[index].start_cluster_high = first_cluster >> 16; buffer_t buffer = calloc(size, 1); rewind(file); fread(buffer, size, 1, file); info->bpb.root_dir_entries++; write_file(info, &root_files[index], buffer, size, disk); write_file(info, 0, (buffer_t)root_files, (info->bpb.root_dir_entries * sizeof(file_t)), disk); free(buffer); } int fat_write_back(FILE *file, fat_info *info){ int res = 0; if(res = write_sector(file, info->bpb.reserved_sectors, info->bpb.sectors_per_fat * info->bpb.fat_count, (buffer_t)info->file_table) == 0){ verbose && printf("Error during write: %d\n", res); } } int detect_repair_fs(FILE *file, fat_info *info){ //perform sanity checks and set data if wrong buffer_t boot_sector = malloc(512); read_sector(file, 0, 1, boot_sector); info->bpb = *(fat_bpb_t *)boot_sector; verbose && printf("\033[0;33mBytes per sector: %d\n", info->bpb.bytes_per_sector); verbose && printf("Sectors per cluster: %x\n", info->bpb.sectors_per_cluster); info->bpb.bytes_per_sector += 512 * info->bpb.bytes_per_sector == 0; info->bpb.sectors_per_cluster += 8 * info->bpb.sectors_per_cluster == 0; verbose && printf("Reserved sectors %d\n", info->bpb.reserved_sectors); info->bpb.reserved_sectors += 32 * info->bpb.reserved_sectors == 0; verbose && printf("Root directory cluster: %d\n", info->bpb.root_dir_cluster); info->bpb.root_dir_cluster += 2 * info->bpb.root_dir_cluster == 0; memcpy(boot_sector, &info->bpb, sizeof(fat_bpb_t)); write_sector(file, 0, 1, boot_sector); info->file_table_size_sectors = info->bpb.fat_count * info->bpb.sectors_per_fat; verbose && printf("\033[1;31mAllocating File Table: 0x%x bytes\033[0m\n", info->bpb.sectors_per_fat * info->bpb.fat_count * info->bpb.bytes_per_sector); info->file_table = calloc(info->bpb.sectors_per_fat * info->bpb.fat_count * info->bpb.bytes_per_sector, 1); read_sector(file, info->bpb.reserved_sectors, info->bpb.sectors_per_fat * info->bpb.fat_count, (buffer_t)info->file_table); if(!info->file_table[info->bpb.root_dir_cluster]){ info->file_table[0] = -1; info->file_table[1] = -1; info->file_table[info->bpb.root_dir_cluster] = -1; } free(boot_sector); } int main(int argc, char **argv){ char list_root_dir = 0; char **files = malloc(512*sizeof(char*)); int filec = 0; FILE *output_file = 0; fat_info info = {}; if(argc == 1 || !strcmp(argv[1], "-h")){ printf("write files to virtual disk\n"); printf("Usage:\ndiskwrite -o "); printf("\n-h: Help"); printf("\n-o: Specify output file\n"); printf("-v: Verbose\n"); } for(int i = 1; i < argc; i++){ if(!strcmp(argv[i], "-l")){ list_root_dir = 1; continue; } if(!strcmp(argv[i], "-o")){ i++; if(i >= argc){ printf("Error: No output file specified!\n"); } verbose && printf("OF: %s\n", argv[i]); output_file = fopen(argv[i], "r+"); if(!output_file){ printf("Error opening file"); } continue; } if(!strcmp(argv[i], "-v")){ verbose = 1; continue; } files[filec++] = argv[i]; verbose && printf("IF: %s\n", argv[i]); } detect_repair_fs(output_file, &info); // if(filec == 0){ // printf("Nothing to do, no files to write\n"); // return 0; // } for(int i = 0; i < filec; i++){ // printf("\033[1;34mWriting file %d in list: %s\033[0m\n", i, files[i]); FILE *f = fopen(files[i], "r"); if(!f){ printf("Error Opening file\n"); exit(-1); } create_file(files[i], &info, f, output_file); fclose(f); } if(list_root_dir){ buffer_t buf = calloc(info.bpb.sectors_per_cluster * 512, 1); read_cluster(&info, 2, buf, info.bpb.sectors_per_cluster * 512, output_file); file_t *root = (void *)buf; uint32_t index = 0; while(root[index].filename[0]){ printf("%s", root[index].filename); printf(".%s\n", root[index].ext); index++; } free(root); } fat_write_back(output_file, &info); verbose && printf("Finished. Exiting...\n"); fclose(output_file); free(info.file_table); free(files); return 0; }