From d9eb5c76032867f2f1f03d591c61c59be799fa7a Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Thu, 28 Mar 2002 15:09:10 +0000 Subject: First version git-svn-id: svn://svn.rockbox.org/rockbox/trunk@60 a1c6a512-1295-4272-9138-f99709370657 --- firmware/fat.c | 1315 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1315 insertions(+) create mode 100644 firmware/fat.c (limited to 'firmware/fat.c') diff --git a/firmware/fat.c b/firmware/fat.c new file mode 100644 index 0000000000..235341f72d --- /dev/null +++ b/firmware/fat.c @@ -0,0 +1,1315 @@ +/*************************************************************************** + * __________ __ ___. + * 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 +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3