2

I have a program and a FAT16 image. Conveniently, the image begins with the boot sector. From there I have extracted the root directory, bytes per sector, and bytes per cluster.

The algorithm for obtaining the byte offset for a subdirectory from the root directory, setting r to the byte offset from image[ 0 ] to the subdirectory entry for a given subfolder named path, is:

    // non-C formatted externally defined values
    image = open_some_fat16_image()
    path = the_name_of_a_directory_whose_parent_is_root()
    LEN_DIR_NAME = 2
    LEN_DIRECTORY_ENTRY = 32
    FREE_ENTRY_MARKER = 0xE5
    END_OF_DIR_MARKER = 0
    OFFSET_DIR_ATTR = 11
    FLAG_DIRECTORY = 0x10
    OFFSET_FIRST_CLUSTER = 26
    current_dir = byte_loc_root;

    unsigned long r = 0;
    for ( int i = 0; i < ( *fat_fs ).root_entry_count && r == 0; i++ )
    {
        // get the name
        char dir_name[ LEN_DIR_NAME +1 ];
        unsigned long byte_loc_dir_name = current_dir + ( i * LEN_DIRECTORY_ENTRY );
        lseek( image, byte_loc_dir_name, SEEK_SET );
        read( image, dir_name, LEN_DIR_NAME );
        dir_name[ LEN_DIR_NAME ] = '\0';

        // is valid entry
        if ( FREE_ENTRY_MARKER == ( unsigned char ) dir_name[ 0 ] ) { continue; }
        if ( END_OF_DIR_MARKER == ( unsigned char ) dir_name[ 0 ] ) { break; }

        // no right whitespace
        for ( int i = 0; i < LEN_DIR_NAME; i ++ )
        {
            if ( ! isspace( dir_name[ i ] ) ) { continue; }
            dir_name[ i ] = '\0';
        }

        // is a match
        if ( 0 != strcmp( dir_name, path ) ) { continue; }

        // is a directory
        unsigned long byte_loc_dir_attr = byte_loc_dir_name + OFFSET_DIR_ATTR;
        lseek( image, byte_loc_dir_attr, SEEK_SET );
        uint8_t attr;
        read( image, &attr, 1 );
        if ( FLAG_DIRECTORY != attr ) { continue; }

        // get cluster
        unsigned long byte_loc_dir_first_cluster = byte_loc_dir_name + OFFSET_FIRST_CLUSTER;
        lseek( image, byte_loc_dir_first_cluster, SEEK_SET );
        uint16_t cluster_idx;
        read( image, & cluster_idx, LEN_FIRST_CLUSTER );

        r = cluster_idx * ( *fat_fs ).sectors_per_cluster * ( *fat_fs ).bytes_per_sector;
    }

I've run this program and have verified that
- sectors_per_cluster ( == 4 ), and
- bytes_per_sector ( == 512 )
match the values in the image ( via hex_editor ). I have also verified that cluster_idx matches what's in the image ( via hex_editor + FAT16 browser ).

The value r = cluster_idx * sectors_per_cluster * bytes_per_sector = 960 * 4 * 512 is set to is: 0x1E0000. Using the FAT16 browser, I was able to find files in the subdirectory argument I gave. With one of those filenames now obtained, I looked for it in the image using a hex_editor. The location of the subdirectory listing was: 0x1ED200.

I'm pretty sure I've obtained all the values correctly, except r. I'm not sure which values to take to obtain the missing 53760 bytes that r is off by. Is there something missing from how r is set?

user2738698
  • 520
  • 7
  • 19
  • This seems like a good complete reference: http://www.maverick-os.dk/FileSystemFormats/FAT16_FileSystem.html – Mark Ransom Apr 08 '16 at 22:53
  • Step1: Insure the return values from I/O functions like `lseek(); read()` are as expected. – chux - Reinstate Monica Apr 08 '16 at 22:56
  • @MarkRansom yeah, I've built the program mostly from that page and the MS white papers. The page says the "first cluster" in directory entries points to the first cluster that the entry occupies after it gets wiped. When I go there, there's nothing but zeroes, and it is way off from where the subdirectory actually is, 53760 B away. – user2738698 Apr 08 '16 at 23:20
  • In the root directory entry for the file in question, the starting cluster for the file is `960`? – Fiddling Bits Apr 08 '16 at 23:33
  • Interesting. 0x1ED200 is not a cluster boundary (0x1ED000 is). – user58697 Apr 09 '16 at 01:00
  • @FiddlingBits yes, read it directly by the program and in a hex editor just in case. Does this mean that image is misformatted? However, when I run it through a FAT16 file browser, the browser can walk around just fine. – user2738698 Apr 09 '16 at 03:32
  • @user58697 I opened up the file in a hex editor and offset 0x1ED000 is filled with the last few sentences of a text file just before turning into a lot of zeroes. – user2738698 Apr 09 '16 at 03:35
  • If the root directory for the file in question says the starting cluster is `960` and the file is not at that cluster, perhaps there's some corruption in the root directory or the file is empty. Doesn't make sense if the file browser works as expected. – Fiddling Bits Apr 09 '16 at 22:50
  • @user58697: there is no requirement that the data clusters are cluster aligned from the beginning of the partition. The boot sector and fats will typically skew things. – MJZ Jul 08 '16 at 04:19

1 Answers1

0

You should send out a dump of the BPB inside the boot sector. See https://en.wikipedia.org/wiki/BIOS_parameter_block and https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#BPB

For FAT 16, the disk is typically laid out as:

boot sector
fat 1
fat 2
root dir
cluster 2
cluster 3
...

So you'll need to adjust:

RootDirSectors = <round up (32 bytes * 
                           BPB.RootDirectoryEntries) to be a multiple of BPB.BytesPerSector> / BPB.BytesPerSector

Cluster2SectorNumber = 
    BPB.ReservedSectors 
     + BPB.NumberOfFats * BPB.SectorsPerFat
     + RootDirSectors

ClusterSectorNumber = 
    Cluster2SectorNumber
     + (cluster_idx - 2) * BPB.SectorsPerCluster

ClusterByteNumber =
    ClusterSectorNumber * BPB.BytesPerSector
MJZ
  • 1,074
  • 6
  • 12