Page 1 of 1

How to go about reading FAT? [SOLVED, free code]

Posted: Wed Mar 21, 2007 11:18 pm
by pcmattman
Ok... So far I can read/write consecutive sectors. But I want a fully functional file i/o system.

This isn't getting the right cluster info:

Code: Select all

// gets the data at the given cluster offset
unsigned short GetClusterData( int cluster, char* fat )
{
	// the cluster number
	int realval = cluster;

	// return the data
	unsigned short data = ( fat[realval+1] << 8 ) | fat[realval];

	// return it
	return data;
}
This is the final step in getting this system working. Any ideas?

Note: this is on FAT16.

Edit: the cluster number I get is wrong, as in really wrong. It seems all the bytes that should be '0' get set to 'F'... Even when they don't, I get a huge number ( > 3000) for the so-called 'next cluster' number... Looking at it in the hex editor I can see that it definitely is not working properly :(

Posted: Thu Mar 22, 2007 2:57 pm
by frank
By the way, did you know that 0xFFFF signaled an end of file in the FAT? no more clusters to read. Other than that have you checked to make sure that the values that are getting passed to GetClusterData is correct ?

Posted: Thu Mar 22, 2007 4:36 pm
by pcmattman
Yes I did know that 0xFFFF signals EOF. When I said all the bits that should be 0 turn to F I meant something like this:

Code: Select all

Hex editor: C9F0
OS swaps bytes...
Return from GetClusterData: FFC9
This is WRONG... it should be returning F0C9 (Which is also wrong...).

I've read through all the FAT docs, and this is what I come up with.

Posted: Thu Mar 22, 2007 5:18 pm
by pcmattman
Ok... I'm seriously confused! For single-sector files, there is no problem, it points to the EOF indicator without a hitch. Then, on multi-sector files, the so-called 'next cluster' value I get is an insanely huge number (or one that points to a null entry).

For instance, a file I created to test the file i/o routines is called MultSect.txt and spans two sectors. According to the cluster information in the directory entry (which I have no problem reading), it's offset in the FAT is at 299... That can't be right, but at offset 298 there's '800C' and at offset 300 there's 'C9F0'. IIRC, I'm meant to flip these bytes, but that still points me to unusual places :( . Any ideas?

Edit: here's the output from my OS, just so you can see what I'm talking about:

Working EOF on a single-sector file:

Code: Select all

Initializing Mattise...                                                        M
Floppy 1: 1.44MB 3.5in floppy 
Floppy 2: No floppy drive. 


Welcome to Mattise! 
Version 1.0 
Please enter your username and password to login... 
Username: isemat 
Password: ******* 
Reading sector 338... (the data area start is at 37) 

Reading cluster at offset 302 
EOF (ffff) 
Welcome, isemat! 
isemat@localhost> $  
Not working on multi-sector file:

Code: Select all

isemat@localhost> $ read MULTSECTTXT                                           M
Reading sector 336... (the data area start is at 37) 

Reading cluster at offset 300 
65481 (ffc9),Reading cluster at offset 65481 
How did we get pointed to a null entry? 
Hello, this is a file that spans two sectors. 

While most of the file reads in the Mattise 
filesystem read consecutive sectors, it is 
useful to be able to read more than one sector 
============================================== 
============================================== 
============================================== 
============================================== 
isemat@localhost> $ 
I just looked at the document Microsoft released on FAT, and it's left more questions than answers... Can someone out there please help me???

Posted: Thu Mar 22, 2007 6:27 pm
by pcmattman
I've re-read the FAT docs (3 times :( ) and so far all I've come up with is this to try to find offsets:

Code: Select all

multsect.txt ==> cluster 299

FAT12:

299 + ( 299 / 2 ) = 448.5
ThisFATSecNum = 1 + ( 0.8759765625 ); = 1
ThisFATEntOffset = 8759765625;

960 + ( 960 / 2 ) = 1440
FAtsecnum = 1 + 2.8125; = 3
fatentoffset = 8125;

(from fat doc)
    if (FATType == FAT12)
        FATOffset = N + (N / 2);    
/* Multiply by 1.5 without using floating point, the divide by 2 rounds DOWN */
    
    ThisFATSecNum = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec);
    ThisFATEntOffset = REM(FATOffset / BPB_BytsPerSec);


OR FAT16:

299 * 2 = 598
fat entry offset = remainder of 598 / 512 = 16796875 (86?)
fat sector number = 1 + 598 / 512 = 2.16... = 2
For a start, the FAT only has data in the first sector. And the numbers I get from the remainder are very wrong. I am completely stumped.

I've been googling for the past two hours, trying out so many ideas and seeing nothing work :evil: Someone, PLEASE tell me what I'm doing wrong!

Posted: Fri Mar 23, 2007 9:39 am
by kernel64
Unfortunately I can't help with that (sorry), but I do know of a couple of good sources of plain English, readable detail on the FAT file system. They are "FreeDOS Kernel; An MS-DOS Emulator for Platform Independence and Embedded Systems Development" by Pat Villani (R&D Books/CMP) and "Assembly Language for Intel Based Computers" 5th Edition by Kip Irvine. If you can afford these books they should be a great help.

You could also try reading the source code for FAT file system drivers in other O/S's to see if you can get the general picture of what they do when reading a file that is larger than one sector. In particular if the code mentions anything in the FAT handling source that has // xxx comments or mentions anything that strays from the official FAT specs from Microsoft.

Posted: Fri Mar 23, 2007 10:01 am
by bubach

Posted: Fri Mar 23, 2007 11:15 am
by deadmutex
I'm not sure how you're calculating the remainders, but from the code that you've shown it looks as if you're confusing a mantissa and a remainder. In this case, you could just use the modulo operator %(or bit shifts depending on the number)

Posted: Fri Mar 23, 2007 6:41 pm
by frank
Have you tried switching from this

Code: Select all

// return the data
   unsigned short data = ( fat[realval+1] << 8 ) | fat[realval]; 
to this:

Code: Select all

// return the data
unsigned short data = ( fat[realval] << 8 ) | fat[realval+1];
That would explain the switching you are seeing.

Re: How to go about reading FAT? (Need serious help!)

Posted: Fri Mar 23, 2007 11:28 pm
by B.E
pcmattman wrote:Ok... So far I can read/write consecutive sectors. But I want a fully functional file i/o system.

This isn't getting the right cluster info:

Code: Select all

// gets the data at the given cluster offset
unsigned short GetClusterData( int cluster, char* fat )
{
	// the cluster number
	int realval = cluster;

	// return the data
	unsigned short data = ( fat[realval+1] << 8 ) | fat[realval];

	// return it
	return data;
}
This is the final step in getting this system working. Any ideas?

Note: this is on FAT16.

Edit: the cluster number I get is wrong, as in really wrong. It seems all the bytes that should be '0' get set to 'F'... Even when they don't, I get a huge number ( > 3000) for the so-called 'next cluster' number... Looking at it in the hex editor I can see that it definitely is not working properly :(
Correct me if I'm wrong, but isn't the n-th entry in the FAT corresponds to the n-th cluster. If so I would say that you've got the size of the fat variable wrong. for example. (I'm not sure what endian FAT uses, but I think it's little endian).

Code: Select all

unsigned short GetClusterData( int cluster, unsigned short* fat){
	return fat[cluster];
}

Posted: Sat Mar 24, 2007 12:06 am
by pcmattman
Well, I've now come up with this after reading the FAT docs:

Code: Select all

// gets the data at the given cluster offset
unsigned short GetClusterData( int cluster, char* fat )
{
	// return value
	unsigned short ret = 0;
	
	// sector block
	char fatblock[512];

	// the cluster entry value
	switch( myFloppy.FileSys )
	{
		case FAT12:
		
			printf( "FAT12_GetClusterData" );
			
			// get the offset
			unsigned short FATOffset = cluster + ( cluster / 2 );
			
			// sector number
			unsigned short ThisFATSecNum = myFloppy.BPB_RsvdSecCnt + ( FATOffset / myFloppy.BPB_BytsPerSec );
			
			// offset in the fat
			unsigned short ThisFATEntOffset = FATOffset % myFloppy.BPB_BytsPerSec;
			
			// tell
			printf( "Reading out of FAT sector %d at offset %d\n", ThisFATSecNum, ThisFATEntOffset );
			
			// read the block
			read_block( ThisFATSecNum, fatblock, 1 );
			
			// get it
			unsigned short FAT12ClustEntryVal = fatblock[ThisFATEntOffset];
			
			// odd/even
			if( cluster & 0x0001 )
			{
				FAT12ClustEntryVal = FAT12ClustEntryVal >> 4;
			}
			else
			{
				FAT12ClustEntryVal = FAT12ClustEntryVal & 0x0FFF;
			}
			
			// set the return
			ret = FAT12ClustEntryVal;
			
			break;
			
		default:
		
			// tell user we can't read anything else YET
			printf( "Only FAT12 is supported at the moment...\n" );
			
			break;
	}
	
	// return to caller
	return ret;
}
It works, as usual, for a single-sector file, not for a multi-sector file AFAIK. IIRC I'm meant to pass the return from this function to the function again to get the next in the chain? And keep going until EOF comes up?

This is an example:

Code: Select all

File MultSect.txt is at sector 404-405 (checked)
Dir entry says at entry 273 (checked)
Adding 31 (start of data area) = 404 (correct)
Using 273 in GetClusterData: returns 7 (wrong)
This is the problem... it only works for one sector :(

Posted: Sat Mar 24, 2007 12:21 am
by pcmattman
Oh... I just got it! For all you having trouble with the same thing, here's how to read FAT12 cluster offsets from the FAT:

Code: Select all

// gets the data at the given cluster offset
unsigned short GetClusterData( int cluster )
{
	// return value
	unsigned short ret = 0;
	
	// sector block
	char fatblock[512];

	// the cluster entry value
	switch( myFloppy.FileSys )
	{
		case FAT12:
		
			printf( "\nFAT12_GetClusterData" );
			printf( "\n" );
			
			// get the offset
			unsigned short FATOffset = cluster + ( cluster / 2 );
			
			// sector number
			unsigned short ThisFATSecNum = myFloppy.BPB_RsvdSecCnt + ( FATOffset / myFloppy.BPB_BytsPerSec );
			
			// offset in the fat
			unsigned short ThisFATEntOffset = FATOffset % myFloppy.BPB_BytsPerSec;
			
			// tell
			printf( "Reading out of FAT sector %d at offset %d\n", ThisFATSecNum, ThisFATEntOffset );
			
			// read the block
			read_block( ThisFATSecNum, fatblock, 1 );
			
			// get it
			unsigned short FAT12ClustEntryVal = * ( ( short* ) &fatblock[ThisFATEntOffset] );
			
			// odd/even
			if( cluster & 0x0001 )
			{
				FAT12ClustEntryVal = FAT12ClustEntryVal >> 4;
			}
			else
			{
				FAT12ClustEntryVal = FAT12ClustEntryVal & 0x0FFF;
			}
			
			// set the return
			ret = FAT12ClustEntryVal;
			
			break;
			
		default:
		
			// tell user we can't read anything else YET
			printf( "Only FAT12 is supported at the moment...\n" );
			
			break;
	}
	
	// return to caller
	return ret;
}
read_block is just the block i/o function:

Code: Select all

void read_block( int block, char* data, int num );
Thankyou all for your help! I think the place where I finally got it was when I found I was trying to get the mantissa instead of the remainder.