📄 fat.c
字号:
fce->secnum = secnum;
#ifdef HAVE_MULTIVOLUME
fce->fat_vol = fat_bpb;
#endif
}
if (dirty)
fce->dirty = true; /* dirt remains, sticky until flushed */
mutex_unlock(&cache_mutex);
return sectorbuf;
}
void fat_recalc_free(IF_MV_NONVOID(int volume))
{
#ifndef HAVE_MULTIVOLUME
const int volume = 0;
#endif
struct bpb* fat_bpb = &fat_bpbs[volume];
long free = 0;
unsigned long i;
#ifdef HAVE_FAT16SUPPORT
if (fat_bpb->is_fat16)
{
for (i = 0; i<fat_bpb->fatsize; i++) {
unsigned int j;
unsigned short* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) {
unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j;
if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
break;
if (letoh16(fat[j]) == 0x0000) {
free++;
if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
fat_bpb->fsinfo.nextfree = c;
}
}
}
}
else
#endif /* #ifdef HAVE_FAT16SUPPORT */
{
for (i = 0; i<fat_bpb->fatsize; i++) {
unsigned int j;
unsigned long* fat = cache_fat_sector(IF_MV2(fat_bpb,) i, false);
for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) {
unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j;
if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */
break;
if (!(letoh32(fat[j]) & 0x0fffffff)) {
free++;
if ( fat_bpb->fsinfo.nextfree == 0xffffffff )
fat_bpb->fsinfo.nextfree = c;
}
}
}
}
fat_bpb->fsinfo.freecount = free;
update_fsinfo(IF_MV(fat_bpb));
}
int fat_find_bootsector( unsigned char *buf )
{
int i;
for (i=0; i<256; i++)
{
rb_fat_read_sectors( i, 1, (unsigned short*)buf );
if (buf[510] == 0x55 && buf[511] == 0xaa &&
(buf[0] == 0xeb || buf[0] == 0xe9))
{
DEBUGF("Found the bpb at sector number %d\n",i);
break;
}
if (i == 255)
{
DEBUGF("Can't find fat boot sector. Exiting ...\n");
return -1;
}
}
return( i );
}
static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb))
{
#ifndef HAVE_MULTIVOLUME
struct bpb* fat_bpb = &fat_bpbs[0];
#endif
if(fat_bpb->bpb_bytspersec != 512)
{
DEBUGF( "bpb_is_sane() - Error: sector size is not 512 (%d)\n",
fat_bpb->bpb_bytspersec);
return -1;
}
if((long)fat_bpb->bpb_secperclus * (long)fat_bpb->bpb_bytspersec > 128L*1024L)
{
DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K "
"(%d * %d = %d)\n",
fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus,
fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus);
return -2;
}
if(fat_bpb->bpb_numfats != 2)
{
DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n",
fat_bpb->bpb_numfats);
}
if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8)
{
DEBUGF( "bpb_is_sane() - Warning: Non-standard "
"media type (0x%02x)\n",
fat_bpb->bpb_media);
}
if(fat_bpb->last_word != 0xaa55)
{
DEBUGF( "bpb_is_sane() - Error: Last word is not "
"0xaa55 (0x%04x)\n", fat_bpb->last_word);
return -3;
}
if (fat_bpb->fsinfo.freecount >
(fat_bpb->totalsectors - fat_bpb->firstdatasector)/
fat_bpb->bpb_secperclus)
{
DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size "
"(0x%04lx)\n", fat_bpb->fsinfo.freecount);
return -4;
}
return 0;
}
int fat_mount(IF_MV2(int volume,) IF_MV2(int drive,) long startsector)
{
#ifndef HAVE_MULTIVOLUME
const int volume = 0;
#endif
struct bpb* fat_bpb = &fat_bpbs[volume];
unsigned char buf[SECTOR_SIZE];
int rc;
long datasec;
#ifdef HAVE_FAT16SUPPORT
int rootdirsectors;
#endif
/* Read the sector */
rc = rb_fat_read_sectors(IF_MV2(drive,) startsector,1,(unsigned short*)buf);
if(rc)
{
DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc);
return rc * 10 - 1;
}
memset(fat_bpb, 0, sizeof(struct bpb));
fat_bpb->startsector = startsector;
#ifdef HAVE_MULTIVOLUME
fat_bpb->drive = drive;
#endif
fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC);
fat_bpb->bpb_secperclus = buf[BPB_SECPERCLUS];
fat_bpb->bpb_rsvdseccnt = BYTES2INT16(buf,BPB_RSVDSECCNT);
fat_bpb->bpb_numfats = buf[BPB_NUMFATS];
fat_bpb->bpb_totsec16 = BYTES2INT16(buf,BPB_TOTSEC16);
fat_bpb->bpb_media = buf[BPB_MEDIA];
fat_bpb->bpb_fatsz16 = BYTES2INT16(buf,BPB_FATSZ16);
fat_bpb->bpb_fatsz32 = BYTES2INT32(buf,BPB_FATSZ32);
fat_bpb->bpb_totsec32 = BYTES2INT32(buf,BPB_TOTSEC32);
fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD);
/* calculate a few commonly used values */
if (fat_bpb->bpb_fatsz16 != 0)
fat_bpb->fatsize = fat_bpb->bpb_fatsz16;
else
fat_bpb->fatsize = fat_bpb->bpb_fatsz32;
if (fat_bpb->bpb_totsec16 != 0)
fat_bpb->totalsectors = fat_bpb->bpb_totsec16;
else
fat_bpb->totalsectors = fat_bpb->bpb_totsec32;
#ifdef HAVE_FAT16SUPPORT
fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT);
rootdirsectors = ((fat_bpb->bpb_rootentcnt * 32)
+ (fat_bpb->bpb_bytspersec - 1)) / fat_bpb->bpb_bytspersec;
#endif /* #ifdef HAVE_FAT16SUPPORT */
fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt
#ifdef HAVE_FAT16SUPPORT
+ rootdirsectors
#endif
+ (fat_bpb->bpb_numfats * fat_bpb->fatsize);
/* Determine FAT type */
datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector;
fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus;
#ifdef TEST_FAT
/*
we are sometimes testing with "illegally small" fat32 images,
so we don't use the proper fat32 test case for test code
*/
if ( fat_bpb->bpb_fatsz16 )
#else
if ( fat_bpb->dataclusters < 65525 )
#endif
{ /* FAT16 */
#ifdef HAVE_FAT16SUPPORT
fat_bpb->is_fat16 = true;
if (fat_bpb->dataclusters < 4085)
{ /* FAT12 */
DEBUGF("This is FAT12. Go away!\n");
return -2;
}
#else /* #ifdef HAVE_FAT16SUPPORT */
DEBUGF("This is not FAT32. Go away!\n");
return -2;
#endif /* #ifndef HAVE_FAT16SUPPORT */
}
#ifdef HAVE_FAT16SUPPORT
if (fat_bpb->is_fat16)
{ /* FAT16 specific part of BPB */
int dirclusters;
fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt
+ (fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16) ;
dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1)
/ fat_bpb->bpb_secperclus); /* rounded up, to full clusters */
/* I assign negative pseudo cluster numbers for the root directory,
their range is counted upward until -1. */
fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data */
fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus
- rootdirsectors;
}
else
#endif /* #ifdef HAVE_FAT16SUPPORT */
{ /* FAT32 specific part of BPB */
fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS);
fat_bpb->bpb_fsinfo = BYTES2INT16(buf,BPB_FSINFO);
fat_bpb->rootdirsector = cluster2sec(IF_MV2(fat_bpb,) fat_bpb->bpb_rootclus);
}
rc = bpb_is_sane(IF_MV(fat_bpb));
if (rc < 0)
{
DEBUGF( "fat_mount() - BPB is not sane\n");
return rc * 10 - 3;
}
#ifdef HAVE_FAT16SUPPORT
if (fat_bpb->is_fat16)
{
fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */
fat_bpb->fsinfo.nextfree = 0xffffffff;
}
else
#endif /* #ifdef HAVE_FAT16SUPPORT */
{
/* Read the fsinfo sector */
rc = rb_fat_read_sectors(IF_MV2(drive,)
startsector + fat_bpb->bpb_fsinfo, 1, (unsigned short*)buf);
if (rc < 0)
{
DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc);
return rc * 10 - 4;
}
fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT);
fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE);
}
/* calculate freecount if unset */
if ( fat_bpb->fsinfo.freecount == 0xffffffff )
{
fat_recalc_free(IF_MV(volume));
}
LDEBUGF("Freecount: %ld\n",fat_bpb->fsinfo.freecount);
LDEBUGF("Nextfree: 0x%lx\n",fat_bpb->fsinfo.nextfree);
LDEBUGF("Cluster count: 0x%lx\n",fat_bpb->dataclusters);
LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus);
LDEBUGF("FAT sectors: 0x%lx\n",fat_bpb->fatsize);
#ifdef HAVE_MULTIVOLUME
fat_bpb->mounted = true;
#endif
return 0;
}
static int parse_direntry(struct fat_direntry *de, const unsigned char *buf)
{
int i=0,j=0;
unsigned char c;
bool lowercase;
memset(de, 0, sizeof(struct fat_direntry));
de->attr = buf[FATDIR_ATTR];
de->crttimetenth = buf[FATDIR_CRTTIMETENTH];
de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE);
de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME);
de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE);
de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME);
de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE);
de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) |
((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16);
/* The double cast is to prevent a sign-extension to be done on CalmRISC16.
(the result of the shift is always considered signed) */
/* fix the name */
lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME);
c = buf[FATDIR_NAME];
if (c == 0x05) /* special kanji char */
c = 0xe5;
i = 0;
while (c != ' ') {
de->name[j++] = lowercase ? tolower(c) : c;
if (++i >= 8)
break;
c = buf[FATDIR_NAME+i];
}
if (buf[FATDIR_NAME+8] != ' ') {
lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT);
de->name[j++] = '.';
for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++)
de->name[j++] = lowercase ? tolower(c) : c;
}
return 1;
}
int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
{
bool done = false;
int i;
int rc;
unsigned char firstbyte;
int longarray[20];
int longs=0;
int sectoridx=0;
unsigned char* cached_buf = dir->sectorcache[0];
dir->entrycount = 0;
while(!done)
{
if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
{
rc = fat_readwrite(&dir->file, 1, cached_buf, false);
if (rc == 0) {
/* eof */
entry->name[0] = 0;
break;
}
if (rc < 0) {
DEBUGF( "fat_getnext() - Couldn't read dir"
" (error code %d)\n", rc);
return rc * 10 - 1;
}
dir->sector = dir->file.lastsector;
}
for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
i < DIR_ENTRIES_PER_SECTOR; i++)
{
unsigned int entrypos = i * DIR_ENTRY_SIZE;
firstbyte = cached_buf[entrypos];
dir->entry++;
if (firstbyte == 0xe5) {
/* free entry */
sectoridx = 0;
dir->entrycount = 0;
continue;
}
if (firstbyte == 0) {
/* last entry */
entry->name[0] = 0;
dir->entrycount = 0;
return 0;
}
dir->entrycount++;
/* longname entry? */
if ( ( cached_buf[entrypos + FATDIR_ATTR] &
FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
longarray[longs++] = entrypos + sectoridx;
}
else {
if ( parse_direntry(entry,
&cached_buf[entrypos]) ) {
/* don't return volume id entry */
if ( entry->attr == FAT_ATTR_VOLUME_ID )
continue;
/* replace shortname with longname? */
if ( longs ) {
int j;
unsigned char* utf8 = entry->name;
/* iterate backwards through the dir entries */
for (j=longs-1; j>=0; j--) {
unsigned char* ptr = cached_buf;
int index = longarray[j];
/* current or cached sector? */
if ( sectoridx >= SECTOR_SIZE ) {
if ( sectoridx >= SECTOR_SIZE*2 ) {
if ( ( index >= SECTOR_SIZE ) &&
( index < SECTOR_SIZE*2 ))
ptr = dir->sectorcache[1];
else
ptr = dir->sectorcache[2];
}
else {
if ( index < SECTOR_SIZE )
ptr = dir->sectorcache[1];
}
index &= SECTOR_SIZE-1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -