Trying to implement the exFAT boot checksum as described in section 3.4 of:
https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
My code does not produce the correct checksums. :(
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main ( int argc, char *argv[] )
{
/* test for filename in parameters */
if ( argc != 2 )
{
/* assume argv[0] has the program name */
printf( "usage: %s filename", argv[0] );
}
else
{
/* assume argv[1] has the filename to process */
FILE *filename = fopen( argv[1], "rb" );
/* check that file exists */
if ( filename == 0 )
{
printf( "Could not open %s\n", argv[1] );
exit (1);
}
else
{
unsigned char cbytes[5632];
int ibytes = fread(cbytes, 1, sizeof(cbytes), filename);
if (ibytes != 5632)
{
printf( "Can't read 5632 bytes from %s\n", argv[1] );
exit (1);
}
fclose( filename );
uint32_t chksum=0;
for (int index = 0; index < 5632; index++)
{
if ((index == 106) || (index == 107) || (index == 112))
{ continue; }
chksum = ((chksum&1) ? 0x80000000 : 0) + (chksum>>1) + cbytes[index];
}
printf("%8x\n", chksum);
}
}
}
Yes I have examined this past question (the author apparently never could get the correct checksums either).
Can anyone spot what I have done wrong?
Supposing that there were
#include <stdint.h>
typedef uint32_t u_int32_t;
appearing before your main()
, there are two primary issues I would see with your code:
As I already remarked in comments, exFAT provides for a variety of sector sizes, and the checksum is over sectors of whatever size is in use on the filesystem. This is why the example code in the MS docs accepts a parameter specifying the sector size. Your code assumes 512-byte sectors, which not only will give you the wrong checksum when a different sector size is in use, but will also lead you to write the wrong amount of checksum data in the wrong location in that case.
Even if the sector size chosen for the particular exFAT filesystem is indeed 512 bytes, you are performing the sum differently from the MS example code in the (likely) event that your implementation's char
is signed rather than unsigned. It would be best to simply declare cbytes
as an array of unsigned char
. Alternatively, with it declared as (signed) char
, casting elements to unsigned char
or uint8_t
would produce an equivalent result -- different in many cases from the result of casting to uint32_t
, which isn't necessary anyway when the value is already being interpreted as unsigned.
Other than those two (significant) factors, your computation appears to be equivalent to the MS example code.
User contributions licensed under CC BY-SA 3.0