| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2002 by Linus Nielsen Feltzing |
| * |
| * All files in this archive are subject to the GNU General Public License. |
| * See the file COPYING in the source tree root for full license agreement. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <math.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| #include <time.h> |
| #include <sys/timeb.h> |
| |
| #include "fakestorage.h" |
| #include "fat.h" |
| #include "debug.h" |
| |
| #define NUM_ROOT_DIR_ENTRIES 512 |
| #define NUM_FATS 2 |
| #define NUM_RESERVED_SECTORS 1 |
| #define NUM_BLOCKS 10000 |
| |
| struct dsksz2secperclus |
| { |
| unsigned int disk_size; |
| unsigned int sec_per_cluster; |
| }; |
| |
| /* |
| ** This is the table for FAT16 drives. NOTE that this table includes |
| ** entries for disk sizes larger than 512 MB even though typically |
| ** only the entries for disks < 512 MB in size are used. |
| ** The way this table is accessed is to look for the first entry |
| ** in the table for which the disk size is less than or equal |
| ** to the DiskSize field in that table entry. For this table to |
| ** work properly BPB_RsvdSecCnt must be 1, BPB_NumFATs |
| ** must be 2, and BPB_RootEntCnt must be 512. Any of these values |
| ** being different may require the first table entries DiskSize value |
| ** to be changed otherwise the cluster count may be to low for FAT16. |
| */ |
| struct dsksz2secperclus dsk_table_fat16 [] = |
| { |
| { 8400, 0}, /* disks up to 4.1 MB, the 0 value for SecPerClusVal |
| trips an error */ |
| { 32680, 2}, /* disks up to 16 MB, 1k cluster */ |
| { 262144, 4}, /* disks up to 128 MB, 2k cluster */ |
| { 524288, 8}, /* disks up to 256 MB, 4k cluster */ |
| { 1048576, 16}, /* disks up to 512 MB, 8k cluster */ |
| /* The entries after this point are not used unless FAT16 is forced */ |
| { 2097152, 32}, /* disks up to 1 GB, 16k cluster */ |
| { 4194304, 64}, /* disks up to 2 GB, 32k cluster */ |
| { 0xFFFFFFFF, 0} /* any disk greater than 2GB, |
| 0 value for SecPerClusVal trips an error */ |
| }; |
| |
| int fat_num_rootdir_sectors(struct bpb *bpb); |
| int fat_first_sector_of_cluster(struct bpb *bpb, unsigned int cluster); |
| int fat_get_fatsize(struct bpb* bpb); |
| int fat_get_totsec(struct bpb* bpb); |
| int fat_get_rootdir_sector(struct bpb *bpb); |
| int fat_first_data_sector(struct bpb* bpb); |
| int fat_get_bpb(struct bpb *bpb); |
| int fat_bpb_is_sane(struct bpb *bpb); |
| int fat_create_fat(struct bpb* bpb); |
| int fat_dbg_read_block(char *name, unsigned char *buf); |
| int fat_flush_fat(struct bpb *bpb); |
| unsigned char *fat_cache_fat_sector(struct bpb *bpb, int secnum); |
| int fat_update_entry(struct bpb *bpb, int entry, unsigned int val); |
| unsigned int fat_getcurrdostime(unsigned short *dosdate, |
| unsigned short *dostime, |
| unsigned char *dostenth); |
| int fat_create_root_dir(struct bpb *bpb); |
| int fat_create_dos_name(unsigned char *name, unsigned char *newname); |
| int fat_create_file(struct bpb *bpb, unsigned int currdir, char *name); |
| |
| unsigned char *fat_cache[256]; |
| int fat_cache_dirty[256]; |
| char current_directory[256] = "\\"; |
| struct bpb *global_bpb; |
| struct disk_info di; |
| |
| extern int yyparse(void); |
| |
| |
| void prompt(void) |
| { |
| printf("C:%s>", current_directory); |
| } |
| |
| #ifdef TEST_FAT |
| int main(int argc, char *argv[]) |
| { |
| struct bpb bpb; |
| |
| memset(fat_cache, 0, sizeof(fat_cache)); |
| memset(fat_cache_dirty, 0, sizeof(fat_cache_dirty)); |
| |
| disk_init(NUM_BLOCKS); |
| |
| di.num_sectors = NUM_BLOCKS; |
| di.sec_per_track = 40; |
| di.num_heads = 250; |
| di.hidden_sectors = 0; |
| |
| if(read_disk("diskdump.dmp") < 0) |
| { |
| printf("*** Warning! The disk is uninitialized\n"); |
| } |
| else |
| { |
| fat_get_bpb(&bpb); |
| } |
| |
| global_bpb = &bpb; |
| prompt(); |
| yyparse(); |
| |
| dump_disk("diskdump.dmp"); |
| return 0; |
| } |
| #endif |
| |
| int fat_sec2cluster(struct bpb *bpb, unsigned int sec) |
| { |
| int first_sec = fat_first_data_sector(bpb); |
| |
| if(sec < first_sec) |
| { |
| fprintf(stderr, "fat_sec2cluster() - Bad sector number (%d)\n", sec); |
| return -1; |
| } |
| |
| return ((sec - first_sec) / bpb->bpb_secperclus) + 2; |
| } |
| |
| int fat_last_cluster_in_chain(struct bpb *bpb, unsigned int cluster) |
| { |
| int iseof = 0; |
| |
| switch(bpb->fat_type) |
| { |
| case FATTYPE_FAT12: |
| if(cluster >= 0x0ff8) |
| iseof = 1; |
| break; |
| case FATTYPE_FAT16: |
| if(cluster >= 0xfff8) |
| iseof = 1; |
| break; |
| case FATTYPE_FAT32: |
| if(cluster >= 0x0ffffff8) |
| iseof = 1; |
| break; |
| } |
| return iseof; |
| } |
| |
| int fat_cluster2sec(struct bpb *bpb, unsigned int cluster) |
| { |
| int max_cluster = (fat_get_totsec(bpb) - fat_first_data_sector(bpb)) / |
| bpb->bpb_secperclus + 1; |
| |
| if(cluster > max_cluster) |
| { |
| fprintf(stderr, "fat_cluster2sec() - Bad cluster number (%d)\n", |
| cluster); |
| return -1; |
| } |
| |
| return fat_first_sector_of_cluster(bpb, cluster); |
| } |
| |
| int fat_first_sector_of_cluster(struct bpb *bpb, unsigned int cluster) |
| { |
| return (cluster - 2) * bpb->bpb_secperclus + fat_first_data_sector(bpb); |
| } |
| |
| int fat_num_rootdir_sectors(struct bpb *bpb) |
| { |
| return ((bpb->bpb_rootentcnt * 32) + (bpb->bpb_bytspersec - 1)) / |
| bpb->bpb_bytspersec; |
| } |
| |
| int fat_get_fatsize(struct bpb* bpb) |
| { |
| if(bpb->bpb_fatsz16 != 0) |
| return bpb->bpb_fatsz16; |
| else |
| return bpb->bpb_fatsz32; |
| } |
| |
| int fat_get_totsec(struct bpb* bpb) |
| { |
| if(bpb->bpb_totsec16 != 0) |
| return bpb->bpb_totsec16; |
| else |
| return bpb->bpb_totsec32; |
| } |
| |
| int fat_get_rootdir_sector(struct bpb *bpb) |
| { |
| return bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fat_get_fatsize(bpb); |
| } |
| |
| int fat_first_data_sector(struct bpb* bpb) |
| { |
| int fatsz; |
| int rootdirsectors; |
| |
| fatsz = fat_get_fatsize(bpb); |
| |
| rootdirsectors = fat_num_rootdir_sectors(bpb); |
| |
| return bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fatsz + rootdirsectors; |
| } |
| |
| int fat_format(struct disk_info *di, char *vol_name) |
| { |
| unsigned char buf[BLOCK_SIZE]; |
| struct bpb bpb; |
| unsigned int root_dir_sectors; |
| unsigned int tmp1, tmp2; |
| int sec_per_clus = 0; |
| int fat_size; |
| int i = 0; |
| int err; |
| |
| while(di->num_sectors > dsk_table_fat16[i].disk_size) |
| { |
| i++; |
| } |
| |
| sec_per_clus = dsk_table_fat16[i].sec_per_cluster; |
| |
| if(sec_per_clus == 0) |
| { |
| fprintf(stderr, "fat_format() - Bad disk size (%u)\n", |
| di->num_sectors); |
| return -1; |
| } |
| |
| /* First calculate how many sectors we need for |
| the root directory */ |
| root_dir_sectors = ((NUM_ROOT_DIR_ENTRIES * 32) + |
| (BLOCK_SIZE - 1)) / BLOCK_SIZE; |
| |
| /* Now calculate the FAT size */ |
| tmp1 = di->num_sectors - (NUM_RESERVED_SECTORS + root_dir_sectors); |
| tmp2 = (256 * sec_per_clus) + NUM_FATS; |
| |
| fat_size = (tmp1 + (tmp2 - 1)) / tmp2; |
| |
| /* Now create the BPB. We must be careful, so we really make |
| it little endian. */ |
| memset(buf, 0xff, BLOCK_SIZE); |
| |
| strncpy(&buf[BS_OEMNAME], "MSWIN4.1", 8); |
| buf[BPB_BYTSPERSEC] = BLOCK_SIZE & 0xff; |
| buf[BPB_BYTSPERSEC+1] = BLOCK_SIZE >> 8; |
| buf[BPB_SECPERCLUS] = sec_per_clus; |
| buf[BPB_RSVDSECCNT] = 1; |
| buf[BPB_RSVDSECCNT+1] = 0; |
| buf[BPB_NUMFATS] = 2; |
| buf[BPB_ROOTENTCNT] = NUM_ROOT_DIR_ENTRIES & 0xff; |
| buf[BPB_ROOTENTCNT+1] = NUM_ROOT_DIR_ENTRIES >> 8; |
| buf[BPB_TOTSEC16] = di->num_sectors & 0xff; |
| buf[BPB_TOTSEC16+1] = di->num_sectors >> 8; |
| buf[BPB_MEDIA] = 0xf0; |
| buf[BPB_FATSZ16] = fat_size & 0xff; |
| buf[BPB_FATSZ16+1] = fat_size >> 8; |
| buf[BPB_SECPERTRK] = di->sec_per_track & 0xff; |
| buf[BPB_SECPERTRK+1] = di->sec_per_track >> 8; |
| buf[BPB_NUMHEADS] = di->num_heads & 0xff; |
| buf[BPB_NUMHEADS+1] = di->num_heads >> 8; |
| buf[BPB_HIDDSEC] = di->hidden_sectors & 0xff; |
| buf[BPB_HIDDSEC+1] = (di->hidden_sectors >> 8) & 0xff; |
| buf[BPB_HIDDSEC+2] = (di->hidden_sectors >> 16) & 0xff; |
| buf[BPB_HIDDSEC+3] = (di->hidden_sectors >> 24) & 0xff; |
| buf[BPB_TOTSEC32] = 0; |
| buf[BPB_TOTSEC32+1] = 0; |
| buf[BPB_TOTSEC32+2] = 0; |
| buf[BPB_TOTSEC32+3] = 0; |
| |
| buf[BS_DRVNUM] = 0; |
| buf[BS_RESERVED1] = 0; |
| buf[BS_BOOTSIG] = 0x29; |
| buf[BS_VOLID] = 0x78; |
| buf[BS_VOLID+1] = 0x56; |
| buf[BS_VOLID+2] = 0x34; |
| buf[BS_VOLID+3] = 0x12; |
| memset(&buf[BS_VOLLAB], ' ', 11); |
| strncpy(&buf[BS_VOLLAB], vol_name, MIN(11, strlen(vol_name)); |
| strncpy(&buf[BS_FILSYSTYPE], "FAT16 ", 8); |
| |
| /* The final signature */ |
| buf[BPB_LAST_WORD] = 0x55; |
| buf[BPB_LAST_WORD+1] = 0xaa; |
| |
| /* Now write the sector to disk */ |
| err = write_block(buf, 0); |
| |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_format() - Couldn't write BSB (error code %i)\n", |
| err); |
| return -1; |
| } |
| |
| if(fat_get_bpb(&bpb) < 0) |
| { |
| fprintf(stderr, "fat_format() - Couldn't read BPB\n"); |
| return -1; |
| } |
| |
| if(fat_create_fat(&bpb) < 0) |
| { |
| fprintf(stderr, "fat_format() - Couldn't create FAT\n"); |
| return -1; |
| } |
| |
| if(fat_create_root_dir(&bpb) < 0) |
| { |
| fprintf(stderr, "fat_format() - Couldn't write root dir sector\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int fat_get_bpb(struct bpb *bpb) |
| { |
| unsigned char buf[BLOCK_SIZE]; |
| int err; |
| int fatsz; |
| int rootdirsectors; |
| int totsec; |
| int datasec; |
| int countofclusters; |
| |
| /* Read the sector */ |
| err = read_block(buf, 0); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_get_bpb() - Couldn't read BPB (error code %i)\n", |
| err); |
| return -1; |
| } |
| |
| memset(bpb, 0, sizeof(struct bpb)); |
| |
| strncpy(bpb->bs_oemname, &buf[BS_OEMNAME], 8); |
| bpb->bs_oemname[8] = 0; |
| |
| bpb->bpb_bytspersec = buf[BPB_BYTSPERSEC] | (buf[BPB_BYTSPERSEC+1] << 8); |
| bpb->bpb_secperclus = buf[BPB_SECPERCLUS]; |
| bpb->bpb_rsvdseccnt = buf[BPB_RSVDSECCNT] | (buf[BPB_RSVDSECCNT+1] << 8); |
| bpb->bpb_numfats = buf[BPB_NUMFATS]; |
| bpb->bpb_rootentcnt = buf[BPB_ROOTENTCNT] | (buf[BPB_ROOTENTCNT+1] << 8); |
| bpb->bpb_totsec16 = buf[BPB_TOTSEC16] | (buf[BPB_TOTSEC16+1] << 8); |
| bpb->bpb_media = buf[BPB_MEDIA]; |
| bpb->bpb_fatsz16 = buf[BPB_FATSZ16] | (buf[BPB_FATSZ16+1] << 8); |
| bpb->bpb_secpertrk = buf[BPB_SECPERTRK] | (buf[BPB_SECPERTRK+1] << 8); |
| bpb->bpb_numheads = buf[BPB_NUMHEADS] | (buf[BPB_NUMHEADS+1] << 8); |
| bpb->bpb_hiddsec = buf[BPB_HIDDSEC] | (buf[BPB_HIDDSEC+1] << 8) | |
| (buf[BPB_HIDDSEC+2] << 16) | (buf[BPB_HIDDSEC+3] << 24); |
| bpb->bpb_totsec32 = buf[BPB_TOTSEC32] | (buf[BPB_TOTSEC32+1] << 8) | |
| (buf[BPB_TOTSEC32+2] << 16) | (buf[BPB_TOTSEC32+3] << 24); |
| |
| bpb->bs_drvnum = buf[BS_DRVNUM]; |
| bpb->bs_bootsig = buf[BS_BOOTSIG]; |
| if(bpb->bs_bootsig == 0x29) |
| { |
| bpb->bs_volid = buf[BS_VOLID] | (buf[BS_VOLID+1] << 8) | |
| (buf[BS_VOLID+2] << 16) | (buf[BS_VOLID+3] << 24); |
| strncpy(bpb->bs_vollab, &buf[BS_VOLLAB], 11); |
| strncpy(bpb->bs_filsystype, &buf[BS_FILSYSTYPE], 8); |
| } |
| |
| bpb->bpb_fatsz32 = (buf[BPB_FATSZ32] + (buf[BPB_FATSZ32+1] << 8)) | |
| (buf[BPB_FATSZ32+2] << 16) | (buf[BPB_FATSZ32+3] << 24); |
| |
| bpb->last_word = buf[BPB_LAST_WORD] | (buf[BPB_LAST_WORD+1] << 8); |
| |
| /* Determine FAT type */ |
| fatsz = fat_get_fatsize(bpb); |
| |
| if(bpb->bpb_totsec16 != 0) |
| totsec = bpb->bpb_totsec16; |
| else |
| totsec = bpb->bpb_totsec32; |
| |
| rootdirsectors = fat_num_rootdir_sectors(bpb); |
| datasec = totsec - (bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fatsz + |
| rootdirsectors); |
| countofclusters = datasec / bpb->bpb_secperclus; |
| |
| if(countofclusters < 4085) |
| { |
| bpb->fat_type = FATTYPE_FAT12; |
| } |
| else |
| { |
| if(countofclusters < 65525) |
| { |
| bpb->fat_type = FATTYPE_FAT16; |
| } |
| else |
| { |
| bpb->fat_type = FATTYPE_FAT32; |
| } |
| } |
| |
| if(fat_bpb_is_sane(bpb) < 0) |
| { |
| fprintf(stderr, "fat_get_bpb() - BPB is not sane\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| int fat_bpb_is_sane(struct bpb *bpb) |
| { |
| if(bpb->fat_type == FATTYPE_FAT32) |
| { |
| fprintf(stderr, "fat_bpb_is_sane() - Error: FAT32 not supported\n"); |
| return -1; |
| } |
| |
| if(bpb->bpb_bytspersec != 512) |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: sector size is not 512 (%i)\n", |
| bpb->bpb_bytspersec); |
| } |
| if(bpb->bpb_secperclus * bpb->bpb_bytspersec > 32768) |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: cluster size is larger than 32K " |
| "(%i * %i = %i)\n", |
| bpb->bpb_bytspersec, bpb->bpb_secperclus, |
| bpb->bpb_bytspersec * bpb->bpb_secperclus); |
| } |
| if(bpb->bpb_rsvdseccnt != 1) |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: Reserved sectors is not 1 (%i)\n", |
| bpb->bpb_rsvdseccnt); |
| } |
| if(bpb->bpb_numfats != 2) |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: NumFATS is not 2 (%i)\n", |
| bpb->bpb_numfats); |
| } |
| if(bpb->bpb_rootentcnt != 512) |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: RootEntCnt is not 512 (%i)\n", |
| bpb->bpb_rootentcnt); |
| } |
| if(bpb->bpb_totsec16 < 200) |
| { |
| if(bpb->bpb_totsec16 == 0) |
| { |
| fprintf(stderr, "fat_bpb_is_sane() - Error: TotSec16 is 0\n"); |
| return -1; |
| } |
| else |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: TotSec16 " |
| "is quite small (%i)\n", |
| bpb->bpb_totsec16); |
| } |
| } |
| if(bpb->bpb_media != 0xf0 && bpb->bpb_media < 0xf8) |
| { |
| fprintf(stderr, |
| "fat_bpb_is_sane() - Warning: Non-standard " |
| "media type (0x%02x)\n", |
| bpb->bpb_media); |
| } |
| if(bpb->last_word != 0xaa55) |
| { |
| fprintf(stderr, "fat_bpb_is_sane() - Error: Last word is not " |
| "0xaa55 (0x%04x)\n", bpb->last_word); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int fat_create_fat(struct bpb* bpb) |
| { |
| unsigned char *sec; |
| int i; |
| int secnum = 0; |
| int fatsz; |
| |
| if(fat_bpb_is_sane(bpb) < 0) |
| { |
| fprintf(stderr, "fat_create_fat() - BPB is not sane\n"); |
| return -1; |
| } |
| |
| if(bpb->bpb_fatsz16 != 0) |
| fatsz = bpb->bpb_fatsz16; |
| else |
| fatsz = bpb->bpb_fatsz32; |
| |
| sec = fat_cache_fat_sector(bpb, secnum); |
| if(!sec) |
| { |
| fprintf(stderr, "fat_create_fat() - Couldn't cache fat sector" |
| " (%d)\n", i); |
| return -1; |
| } |
| |
| fat_cache_dirty[secnum] = 1; |
| |
| /* First entry should have the media type in the |
| low byte and the rest of the bits set to 1. |
| The second should be the EOC mark. */ |
| memset(sec, 0, BLOCK_SIZE); |
| sec[0] = bpb->bpb_media; |
| if(bpb->fat_type == FATTYPE_FAT12) |
| { |
| sec[1] = 0xff; |
| sec[2] = 0xff; |
| } |
| if(bpb->fat_type == FATTYPE_FAT16) |
| { |
| sec[0] = bpb->bpb_media; |
| sec[1] = 0xff; |
| sec[2] = 0xff; |
| sec[3] = 0xff; |
| } |
| secnum++; |
| |
| for(i = 0; i < fatsz - 1;i++) |
| { |
| sec = fat_cache_fat_sector(bpb, secnum); |
| if(!sec) |
| { |
| fprintf(stderr, "fat_create_fat() - Couldn't cache fat sector" |
| " (%d)\n", i); |
| return -1; |
| } |
| fat_cache_dirty[secnum] = 1; |
| secnum++; |
| memset(sec, 0, BLOCK_SIZE); |
| } |
| |
| if(fat_flush_fat(bpb) < 0) |
| { |
| fprintf(stderr, "fat_create_fat() - Couldn't flush fat\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int fat_dbg_read_block(char *name, unsigned char *buf) |
| { |
| FILE *f; |
| |
| f = fopen(name, "rb"); |
| if(f) |
| { |
| if(fread(buf, 1, 512, f) != 512) |
| { |
| fprintf(stderr, "Could not read file \"%s\"\n", name); |
| fclose(f); |
| return -1; |
| } |
| /* Now write the sector to disk */ |
| write_block(buf, 0); |
| fclose(f); |
| } |
| else |
| { |
| fprintf(stderr, "Could not open file \"%s\"\n", name); |
| return -1; |
| } |
| return 0; |
| } |
| |
| unsigned char *fat_cache_fat_sector(struct bpb *bpb, int secnum) |
| { |
| unsigned char *sec; |
| |
| sec = fat_cache[secnum]; |
| /* Load the sector if it is not cached */ |
| if(!sec) |
| { |
| sec = malloc(bpb->bpb_bytspersec); |
| if(!sec) |
| { |
| fprintf(stderr, "fat_cache_fat_sector() - Out of memory\n"); |
| return NULL; |
| } |
| if(read_block(sec, secnum + bpb->bpb_rsvdseccnt) < 0) |
| { |
| fprintf(stderr, "fat_cache_fat_sector() - Could" |
| " not read sector %d\n", |
| secnum); |
| free(sec); |
| return NULL; |
| } |
| fat_cache[secnum] = sec; |
| } |
| return sec; |
| } |
| |
| int fat_update_entry(struct bpb *bpb, int entry, unsigned int val) |
| { |
| unsigned char *sec; |
| unsigned char *sec2; |
| int fatsz; |
| int fatoffset; |
| int thisfatsecnum; |
| int thisfatentoffset; |
| unsigned int tmp; |
| |
| fatsz = fat_get_fatsize(bpb); |
| |
| if(bpb->fat_type == FATTYPE_FAT12) |
| { |
| fatoffset = entry + (entry / 2); |
| } |
| else |
| { |
| if(bpb->fat_type == FATTYPE_FAT16) |
| fatoffset = entry * 2; |
| else |
| fatoffset = entry * 4; |
| } |
| thisfatsecnum = fatoffset / bpb->bpb_bytspersec; |
| thisfatentoffset = fatoffset % bpb->bpb_bytspersec; |
| |
| sec = fat_cache_fat_sector(bpb, thisfatsecnum); |
| /* Load the sector if it is not cached */ |
| if(!sec) |
| { |
| fprintf(stderr, "fat_update_entry() - Could not cache sector %d\n", |
| thisfatsecnum); |
| return -1; |
| } |
| |
| fat_cache_dirty[thisfatsecnum] = 1; |
| |
| switch(bpb->fat_type) |
| { |
| case FATTYPE_FAT12: |
| if(thisfatentoffset == bpb->bpb_bytspersec - 1) |
| { |
| /* This entry spans a sector boundary. Take care */ |
| sec2 = fat_cache_fat_sector(bpb, thisfatsecnum + 1); |
| /* Load the sector if it is not cached */ |
| if(!sec2) |
| { |
| fprintf(stderr, "fat_update_entry() - Could not " |
| "cache sector %d\n", |
| thisfatsecnum + 1); |
| return -1; |
| } |
| fat_cache_dirty[thisfatsecnum + 1] = 1; |
| } |
| else |
| { |
| if(entry & 1) /* Odd entry number? */ |
| { |
| tmp = sec[thisfatentoffset] & 0xf0; |
| sec[thisfatentoffset] = tmp | (val & 0x0f); |
| sec[thisfatentoffset+1] = (val >> 4) & 0xff; |
| } |
| else |
| { |
| sec[thisfatentoffset] = val & 0xff; |
| tmp = sec[thisfatentoffset+1] & 0x0f; |
| sec[thisfatentoffset+1] = tmp | ((val >> 4) & 0xf0); |
| } |
| } |
| break; |
| case FATTYPE_FAT16: |
| *(unsigned short *)(&sec[thisfatentoffset]) = val; |
| break; |
| case FATTYPE_FAT32: |
| tmp = *(unsigned short *)(&sec[thisfatentoffset]) & 0xf000000; |
| val = tmp | (val & 0x0fffffff); |
| *(unsigned short *)(&sec[thisfatentoffset]) = val; |
| break; |
| } |
| return 0; |
| } |
| |
| int fat_read_entry(struct bpb *bpb, int entry) |
| { |
| unsigned char *sec; |
| unsigned char *sec2; |
| int fatsz; |
| int fatoffset; |
| int thisfatsecnum; |
| int thisfatentoffset; |
| unsigned int val; |
| |
| fatsz = fat_get_fatsize(bpb); |
| |
| if(bpb->fat_type == FATTYPE_FAT12) |
| { |
| fatoffset = entry + (entry / 2); |
| } |
| else |
| { |
| if(bpb->fat_type == FATTYPE_FAT16) |
| fatoffset = entry * 2; |
| else |
| fatoffset = entry * 4; |
| } |
| thisfatsecnum = fatoffset / bpb->bpb_bytspersec; |
| thisfatentoffset = fatoffset % bpb->bpb_bytspersec; |
| |
| sec = fat_cache_fat_sector(bpb, thisfatsecnum); |
| /* Load the sector if it is not cached */ |
| if(!sec) |
| { |
| fprintf(stderr, "fat_update_entry() - Could not cache sector %d\n", |
| thisfatsecnum); |
| return -1; |
| } |
| |
| switch(bpb->fat_type) |
| { |
| case FATTYPE_FAT12: |
| if(thisfatentoffset == bpb->bpb_bytspersec - 1) |
| { |
| /* This entry spans a sector boundary. Take care */ |
| sec2 = fat_cache_fat_sector(bpb, thisfatsecnum + 1); |
| /* Load the sector if it is not cached */ |
| if(!sec2) |
| { |
| fprintf(stderr, "fat_update_entry() - Could not " |
| "cache sector %d\n", |
| thisfatsecnum + 1); |
| return -1; |
| } |
| } |
| else |
| { |
| if(entry & 1) /* Odd entry number? */ |
| { |
| val = (sec[thisfatentoffset] & 0x0f) | |
| (sec[thisfatentoffset+1] << 4); |
| } |
| else |
| { |
| val = (sec[thisfatentoffset] & 0xff) | |
| ((sec[thisfatentoffset+1] & 0x0f) << 8); |
| } |
| } |
| break; |
| case FATTYPE_FAT16: |
| val = *(unsigned short *)(&sec[thisfatentoffset]); |
| break; |
| case FATTYPE_FAT32: |
| val = *(unsigned int *)(&sec[thisfatentoffset]); |
| break; |
| } |
| return val; |
| } |
| |
| int fat_flush_fat(struct bpb *bpb) |
| { |
| int i; |
| int err; |
| unsigned char *sec; |
| int fatsz; |
| unsigned short d, t; |
| char m; |
| |
| fatsz = fat_get_fatsize(bpb); |
| |
| for(i = 0;i < 256;i++) |
| { |
| if(fat_cache[i] && fat_cache_dirty[i]) |
| { |
| printf("Flushing FAT sector %d\n", i); |
| sec = fat_cache[i]; |
| err = write_block(sec, i + bpb->bpb_rsvdseccnt); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_flush_fat() - Couldn't write" |
| " sector (%d)\n", i + bpb->bpb_rsvdseccnt); |
| return -1; |
| } |
| err = write_block(sec, i + bpb->bpb_rsvdseccnt + fatsz); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_flush_fat() - Couldn't write" |
| " sector (%d)\n", i + bpb->bpb_rsvdseccnt + fatsz); |
| return -1; |
| } |
| fat_cache_dirty[i] = 0; |
| } |
| } |
| |
| fat_getcurrdostime(&d, &t, &m); |
| return 0; |
| } |
| |
| unsigned int fat_getcurrdostime(unsigned short *dosdate, |
| unsigned short *dostime, |
| unsigned char *dostenth) |
| { |
| struct timeb tb; |
| struct tm *tm; |
| |
| ftime(&tb); |
| tm = localtime(&tb.time); |
| |
| *dosdate = ((tm->tm_year - 80) << 9) | |
| ((tm->tm_mon + 1) << 5) | |
| (tm->tm_mday); |
| |
| *dostime = (tm->tm_hour << 11) | |
| (tm->tm_min << 5) | |
| (tm->tm_sec >> 1); |
| |
| *dostenth = (tm->tm_sec & 1) * 100 + tb.millitm / 10; |
| return 0; |
| } |
| |
| int fat_create_root_dir(struct bpb *bpb) |
| { |
| unsigned char buf[BLOCK_SIZE]; |
| int fatsz; |
| int sec; |
| int res; |
| int i; |
| unsigned short dosdate; |
| unsigned short dostime; |
| unsigned char dostenth; |
| int num_root_sectors; |
| |
| fatsz = fat_get_fatsize(bpb); |
| |
| sec = bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fatsz; |
| |
| memset(buf, 0, sizeof(buf)); |
| |
| strncpy(&buf[FATDIR_NAME], bpb->bs_vollab, 11); |
| buf[FATDIR_ATTR] = FAT_ATTR_VOLUME_ID; |
| buf[FATDIR_NTRES] = 0; |
| |
| fat_getcurrdostime(&dosdate, &dostime, &dostenth); |
| buf[FATDIR_WRTDATE] = dosdate & 0xff; |
| buf[FATDIR_WRTDATE+1] = dosdate >> 8; |
| buf[FATDIR_WRTTIME] = dostime & 0xff; |
| buf[FATDIR_WRTTIME+1] = dostime >> 8; |
| |
| printf("Writing rootdir to sector %d...\n", sec); |
| |
| res = write_block(buf, sec); |
| if(res < 0) |
| { |
| fprintf(stderr, "fat_create_root_dir() - Couldn't write sector (%d)\n", |
| sec); |
| return -1; |
| } |
| |
| printf("Clearing the rest of the root dir.\n"); |
| sec++; |
| num_root_sectors = bpb->bpb_rootentcnt * 32 / bpb->bpb_bytspersec; |
| memset(buf, 0, BLOCK_SIZE); |
| |
| for(i = 1;i < num_root_sectors;i++) |
| { |
| if(write_block(buf, sec++) < 0) |
| { |
| fprintf(stderr, "fat_create_root_dir() - " |
| " Couldn't write sector (%d)\n", sec); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| int fat_get_next_cluster(struct bpb *bpb, unsigned int cluster) |
| { |
| int next_cluster = fat_read_entry(bpb, cluster); |
| |
| if(fat_last_cluster_in_chain(bpb, next_cluster)) |
| return 0; |
| else |
| return next_cluster; |
| } |
| |
| int fat_add_dir_entry(struct bpb *bpb, unsigned int currdir, |
| struct fat_direntry *de) |
| { |
| unsigned char buf[BLOCK_SIZE]; |
| unsigned char *eptr; |
| int i; |
| int err; |
| unsigned int sec; |
| unsigned int sec_cnt; |
| int need_to_update_last_empty_marker = 0; |
| int is_rootdir = (currdir == 0); |
| int done = 0; |
| unsigned char firstbyte; |
| |
| if(is_rootdir) |
| { |
| sec = fat_get_rootdir_sector(bpb); |
| } |
| else |
| { |
| sec = fat_first_sector_of_cluster(bpb, currdir); |
| } |
| |
| sec_cnt = 0; |
| |
| while(!done) |
| { |
| /* The root dir has a fixed size */ |
| if(is_rootdir) |
| { |
| if(sec_cnt >= bpb->bpb_rootentcnt * 32 / bpb->bpb_bytspersec) |
| { |
| /* We have reached the last sector of the root dir */ |
| if(need_to_update_last_empty_marker) |
| { |
| /* Since the directory is full, there is no room for |
| a marker, so we just exit */ |
| return 0; |
| } |
| else |
| { |
| fprintf(stderr, "fat_add_dir_entry() -" |
| " Root dir is full\n"); |
| return -1; |
| } |
| } |
| } |
| else |
| { |
| if(sec_cnt >= bpb->bpb_secperclus) |
| { |
| /* We have reached the end of this cluster */ |
| printf("Moving to the next cluster..."); |
| currdir = fat_get_next_cluster(bpb, currdir); |
| printf("new cluster is %d\n", currdir); |
| |
| if(!currdir) |
| { |
| /* This was the last in the chain, |
| we have to allocate a new cluster */ |
| /* TODO */ |
| } |
| } |
| } |
| |
| printf("Reading sector %d...\n", sec); |
| /* Read the next sector in the current dir */ |
| err = read_block(buf, sec); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_add_dir_entry() - Couldn't read dir sector" |
| " (error code %i)\n", err); |
| return -1; |
| } |
| |
| if(need_to_update_last_empty_marker) |
| { |
| /* All we need to do is to set the first entry to 0 */ |
| printf("Clearing the first entry in sector %d\n", sec); |
| buf[0] = 0; |
| done = 1; |
| } |
| else |
| { |
| /* Look for a free slot */ |
| for(i = 0;i < BLOCK_SIZE;i+=32) |
| { |
| firstbyte = buf[i]; |
| if(firstbyte == 0xe5 || firstbyte == 0) |
| { |
| printf("Found free slot at entry %d in sector %d\n", |
| i/32, sec); |
| eptr = &buf[i]; |
| memset(eptr, 0, 32); |
| strncpy(&eptr[FATDIR_NAME], de->name, 11); |
| eptr[FATDIR_ATTR] = de->attr; |
| eptr[FATDIR_NTRES] = 0; |
| |
| eptr[FATDIR_CRTTIMETENTH] = de->crttimetenth; |
| eptr[FATDIR_CRTDATE] = de->crtdate & 0xff; |
| eptr[FATDIR_CRTDATE+1] = de->crtdate >> 8; |
| eptr[FATDIR_CRTTIME] = de->crttime & 0xff; |
| eptr[FATDIR_CRTTIME+1] = de->crttime >> 8; |
| |
| eptr[FATDIR_WRTDATE] = de->wrtdate & 0xff; |
| eptr[FATDIR_WRTDATE+1] = de->wrtdate >> 8; |
| eptr[FATDIR_WRTTIME] = de->wrttime & 0xff; |
| eptr[FATDIR_WRTTIME+1] = de->wrttime >> 8; |
| |
| eptr[FATDIR_FILESIZE] = de->filesize & 0xff; |
| eptr[FATDIR_FILESIZE+1] = (de->filesize >> 8) & 0xff; |
| eptr[FATDIR_FILESIZE+2] = (de->filesize >> 16) & 0xff; |
| eptr[FATDIR_FILESIZE+3] = (de->filesize >> 24) & 0xff; |
| |
| /* Advance the last_empty_entry marker */ |
| if(firstbyte == 0) |
| { |
| i += 32; |
| if(i < BLOCK_SIZE) |
| { |
| buf[i] = 0; |
| /* We are done */ |
| done = 1; |
| } |
| else |
| { |
| /* We must fill in the first entry |
| in the next sector */ |
| need_to_update_last_empty_marker = 1; |
| } |
| } |
| |
| err = write_block(buf, sec); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_add_dir_entry() - " |
| " Couldn't write dir" |
| " sector (error code %i)\n", err); |
| return -1; |
| } |
| break; |
| } |
| } |
| } |
| sec++; |
| sec_cnt++; |
| } |
| |
| return 0; |
| } |
| |
| unsigned char fat_char2dos(unsigned char c) |
| { |
| switch(c) |
| { |
| case 0xe5: /* Special kanji character */ |
| c = 0x05; |
| break; |
| case 0x22: |
| case 0x2a: |
| case 0x2b: |
| case 0x2c: |
| case 0x2e: |
| case 0x3a: |
| case 0x3b: |
| case 0x3c: |
| case 0x3d: |
| case 0x3e: |
| case 0x3f: |
| case 0x5b: |
| case 0x5c: |
| case 0x5d: |
| case 0x7c: |
| /* Illegal name */ |
| c = 0; |
| break; |
| |
| default: |
| if(c < 0x20) |
| { |
| /* Illegal name */ |
| c = 0; |
| } |
| break; |
| } |
| return c; |
| } |
| |
| int fat_create_dos_name(unsigned char *name, unsigned char *newname) |
| { |
| unsigned char n[12]; |
| unsigned char c; |
| int i; |
| char *ext; |
| |
| if(strlen(name) > 12) |
| { |
| return -1; |
| } |
| |
| strcpy(n, name); |
| |
| ext = strchr(n, '.'); |
| if(ext) |
| { |
| *ext++ = 0; |
| } |
| |
| /* The file name is either empty, or there was only an extension. |
| In either case it is illegal. */ |
| if(n[0] == 0) |
| { |
| return -1; |
| } |
| |
| /* Name part */ |
| for(i = 0;n[i] && (i < 8);i++) |
| { |
| c = fat_char2dos(n[i]); |
| if(c) |
| { |
| newname[i] = toupper(c); |
| } |
| } |
| while(i < 8) |
| { |
| newname[i++] = ' '; |
| } |
| |
| /* Extension part */ |
| for(i = 0;ext && ext[i] && (i < 3);i++) |
| { |
| c = fat_char2dos(ext[i]); |
| if(c) |
| { |
| newname[8+i] = toupper(c); |
| } |
| } |
| while(i < 3) |
| { |
| newname[8+i++] = ' '; |
| } |
| return 0; |
| } |
| |
| int fat_create_dir(struct bpb *bpb, unsigned int currdir, char *name) |
| { |
| struct fat_direntry de; |
| int err; |
| |
| printf("fat_create_file()\n"); |
| memset(&de, 0, sizeof(struct fat_direntry)); |
| if(fat_create_dos_name(name, de.name) < 0) |
| { |
| fprintf(stderr, "fat_create_file() - Illegal file name (%s)\n", name); |
| return -1; |
| } |
| |
| fat_getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); |
| de.wrtdate = de.crtdate; |
| de.wrttime = de.crttime; |
| de.filesize = 0; |
| de.attr = FAT_ATTR_DIRECTORY; |
| |
| err = fat_add_dir_entry(bpb, currdir, &de); |
| return 0; |
| } |
| |
| int fat_create_file(struct bpb *bpb, unsigned int currdir, char *name) |
| { |
| struct fat_direntry de; |
| int err; |
| |
| printf("fat_create_file()\n"); |
| memset(&de, 0, sizeof(struct fat_direntry)); |
| if(fat_create_dos_name(name, de.name) < 0) |
| { |
| fprintf(stderr, "fat_create_file() - Illegal file name (%s)\n", name); |
| return -1; |
| } |
| fat_getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); |
| de.wrtdate = de.crtdate; |
| de.wrttime = de.crttime; |
| de.filesize = 0; |
| |
| err = fat_add_dir_entry(bpb, currdir, &de); |
| return err; |
| } |
| |
| void fat_fill_direntry(struct fat_direntry *de, char *buf) |
| { |
| memset(de, 0, sizeof(struct fat_direntry)); |
| |
| strncpy(de->name, &buf[FATDIR_NAME], 11); |
| de->attr = buf[FATDIR_ATTR]; |
| de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; |
| de->crtdate = buf[FATDIR_CRTDATE] | (buf[FATDIR_CRTDATE+1] << 8); |
| de->crttime = buf[FATDIR_CRTTIME] | (buf[FATDIR_CRTTIME+1] << 8); |
| de->wrtdate = buf[FATDIR_WRTDATE] | (buf[FATDIR_WRTDATE+1] << 8); |
| de->wrttime = buf[FATDIR_WRTTIME] | (buf[FATDIR_WRTTIME+1] << 8); |
| |
| de->filesize = buf[FATDIR_FILESIZE] | |
| (buf[FATDIR_FILESIZE+1] << 8) | |
| (buf[FATDIR_FILESIZE+2] << 16) | |
| (buf[FATDIR_FILESIZE+3] << 24); |
| } |
| |
| int fat_opendir(struct bpb *bpb, struct fat_dirent *ent, unsigned int currdir) |
| { |
| int is_rootdir = (currdir == 0); |
| unsigned int sec; |
| int err; |
| |
| if(is_rootdir) |
| { |
| sec = fat_get_rootdir_sector(bpb); |
| } |
| else |
| { |
| sec = fat_first_sector_of_cluster(bpb, currdir); |
| } |
| |
| /* Read the first sector in the current dir */ |
| err = read_block(ent->cached_buf, sec); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_getfirst() - Couldn't read dir sector" |
| " (error code %i)\n", err); |
| return -1; |
| } |
| |
| ent->entry = 0; |
| ent->cached_sec = sec; |
| ent->num_sec = 0; |
| return 0; |
| } |
| |
| int fat_getnext(struct bpb *bpb, struct fat_dirent *ent, |
| struct fat_direntry *entry) |
| { |
| int done = 0; |
| int i; |
| int err; |
| unsigned char firstbyte; |
| |
| while(!done) |
| { |
| /* Look for a free slot */ |
| for(i = ent->entry;i < BLOCK_SIZE/32;i++) |
| { |
| firstbyte = ent->cached_buf[i*32]; |
| if(firstbyte == 0xe5) |
| { |
| continue; |
| } |
| |
| if(firstbyte == 0) |
| { |
| return -1; |
| } |
| |
| fat_fill_direntry(entry, &ent->cached_buf[i*32]); |
| done = 1; |
| break; |
| } |
| |
| /* Next sector? */ |
| if(i >= BLOCK_SIZE/32) |
| { |
| ent->num_sec++; |
| ent->cached_sec++; |
| |
| /* Do we need to advance one cluster? */ |
| if(ent->num_sec >= bpb->bpb_secperclus) |
| { |
| ent->num_sec = 0; |
| ent->cached_sec = fat_get_next_cluster( |
| bpb, fat_sec2cluster(bpb, ent->cached_sec)); |
| if(!ent->cached_sec) |
| { |
| printf("End of cluster chain.\n"); |
| return -1; |
| } |
| } |
| |
| /* Read the next sector */ |
| err = read_block(ent->cached_buf, ent->cached_sec); |
| if(err < 0) |
| { |
| fprintf(stderr, "fat_getnext() - Couldn't read dir sector" |
| " (error code %i)\n", err); |
| return -1; |
| } |
| |
| i = 0; |
| } |
| else |
| { |
| i++; |
| } |
| ent->entry = i; |
| } |
| return 0; |
| } |