finding root from boot parameter block

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Hi guys, I'm back to finish my FAT32 driver.  Have a look at this and tell me how the root cluster points to address 0x2000000..

 

Thanks guys...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does line 186 here help?...

 

https://spaces.microchip.com/gf/project/sdbootloader/scmsvn/?action=browse&path=%2Ftrunk%2Fmain.c&view=markup&revision=22

 

In my bootloader I have what I believe to be the very minimal amount of disk access simply to find the root directory and then a file within it. As I have probably said previously the key to all this is FatGen103.doc. It really DOES have all the answers. The equation I use:

 RootDir = BPBSec + RsvdSecCnt + (Buff[BPB_NumFATs] * FATSz);

Makes use of the official names for the BPB fields given in fatGen103. It's a bit of a shame that your analysis program above is not using the same nomenclature. However I guess:

BPBSec = the LBA that you are looking at here - I can't immediately tell from the display which sector it is you are looking at
RsvdSecCnt = "Resereved Sectors" = 4726
BPB_NumFATs = "Number of FATs" = 2
FATSz = "Sectors per FAT" = 30,405

so I guess you are looking at:

Root = BPB_LBA + 4726 + (2 * 30405)
Root = BPB_LBA + 4726 + 60810
Root = BPB_LBA + 65536

(did you see what they did there? they've set the RsvdSecCnt to make sure the Root falls exactly on a large binary boundary ;)

 

So if this BPB is actually sector 0 then I would expect to find the root at LBA 65536 which (multiplied by 512 to make a byte offset) is byte 33554432 which is 0x02000000

 

As you say you are directed to 0x02000000 then I think I can unequivocally say this BPB is in sector 0!

 

PS really, like I say, Fatgen103 is totally complete - it's one of the few things Microsoft actually got right (though I suspect the "103" suggests a few attempts?) - you can decode and even write to a FAT system using nothing more than the info in FatGen103. Having said that I find the pages on Wikipedia about MBRs and boot/BPBs really makes life simple!

Last Edited: Fri. Mar 29, 2019 - 09:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Clawson.

 

PS: I fly model aeroplanes also.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Clawson,

 

What would be the best way to parse the files and folder information from a sector?

 

Wm.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well once you've found the root all the entries are 32 bytes. The first byte is 0x00 for the first unused slot and it's 0XE5 for anything that's been deleted. Otherwise it should be a valid file or directory (the attribute bits say which). if it's a dir then get the AU and do the whole thing over again. When reading a dir it may (likely will) span sectors. You can just keep reading adjacent sectors until you hit an AU boundary then you need to return to the FAT to locate the next AU. In FAT32 the root is no different to other dirs and can span multiple AUs.
.
Note that if you try to support LFN the whole thing gets a lot more complicated (and you risk violating Microsoft's patent - FAT is not patented but LFN are though the 30 year patent life may now have expired).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Answering your PM here:

 locate folders I can check the name field for 0x20, 0x20, 0x20.  And to parse the file enteries I can check for '.'

Err no. I can have a directory called mydir.dat so just looking for . is not the way (and  anyway, in 8.3 (non LFN) naming there is NO dot anyway - it's simply implied as the 9th character).

 

File entries are documented here:

 

https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Directory_entry

 

Specifically look at offset 0x0B. It is

 

Bit Mask Description
0 0x01 Read Only. (Since DOS 2.0) If this bit is set, the operating system will not allow a file to be opened for modification.

 

Deliberately setting this bit for files which will not be written to (executables, shared libraries and data files) may help avoid problems with concurrent file access in multi-tasking, multi-user or network environments with applications not specifically designed to work in such environments (i.e. non-SHARE-enabled programs).

The DCF digital camera file system standard utilizes the Read Only attribute to allow directories or individual files (DCF objects) to be marked as "protected" from deletion by the user.[4]

1 0x02 Hidden. Hides files or directories from normal directory views.

 

Under DR DOS 3.31 and higher, under PalmDOS, Novell DOS, OpenDOS, Concurrent DOS, Multiuser DOS, REAL/32, password protected files and directories also have the hidden attribute set.[52] Password-aware operating systems should not hide password-protected files from directory views, even if this bit may be set. The password protection mechanism does not depend on the hidden attribute being set up to including DR-DOS 7.03, but if the hidden attribute is set, it should not be cleared for any password-protected files.

2 0x04 System. Indicates that the file belongs to the system and must not be physically moved (e.g., during defragmentation), because there may be references into the file using absolute addressing bypassing the file system (boot loaders, kernel images, swap files, extended attributes, etc.).
3 0x08 Volume Label. (Since MS-DOS 1.28 and PC DOS 2.0) Indicates an optional directory volume label, normally only residing in a volume's root directory. Ideally, the volume label should be the first entry in the directory (after reserved entries) in order to avoid problems with VFAT LFNs. If this volume label is not present, some systems may fall back to display the partition volume label instead, if an EBPB is present in the boot sector (not present with some non-bootable block device drivers, and possibly not writeable with boot sector write protection). Even if this volume label is present, partitioning tools like FDISK may display the partition volume label instead. The entry occupies a directory entry but has no file associated with it. Volume labels have a filesize entry of zero.

 

Pending delete files and directories under DELWATCH have the volume attribute set until they are purged or undeleted.[52]

4 0x10 Subdirectory. (Since MS-DOS 1.40 and PC DOS 2.0) Indicates that the cluster-chain associated with this entry gets interpreted as subdirectory instead of as a file. Subdirectories have a filesize entry of zero.
5 0x20 Archive. (Since DOS 2.0) Typically set by the operating system as soon as the file is created or modified to mark the file as "dirty", and reset by backup software once the file has been backed up to indicate "pure" state.
6 0x40 Device (internally set for character device names found in filespecs, never found on disk), must not be changed by disk tools.
7 0x80 Reserved, must not be changed by disk tools.

Fatgen103 describes the same thing thus:

 

 

So if bit 4 is set (0x10) then it's a directory entry - otherwise it's a file. So to read directory entries I'd do something like:

typedef enum {
    ATTR_READ_ONLY = 0x01,
    ATTR_HIDDEN = 0x02,
    ATTR_SYSTEM = 0x04,
    ATTR_VOLUME_ID = 0x08,
    ATTR_DIRECTORY = 0x10,
    ATTR_ARCHIVE = 0x20
} fattr_e;

#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)

typedef struct {
    char name[8];
    char ext[3];
} dirname_t;

typedef struct {
    dirname_t DIR_Name;
    fattr_e DIR_Attr;
    uint8_t DIR_NTRes;
    uint8_t DIR_CrcTimeTenth;
    uint16_t DIR_CrtTime;
    uint16_t DIR_CrtDate;
    uint16_t DIR_LastAccDate;
    uint16_t DIR_FstClustHI;
    uint16_t DIR_WrtTime;
    uint16_t DIR_WrtDate;
    uint16_t DIR_FstClustLO;
    uint32_t DIR_FileSize;
} dirent_t;

dirent_t DIR_entry;

for (i=0; i < 512; i += 32) {
    fread(&DIR_entry, sizeof(DIR_entry), fin);
    if (DIR_entry.DIR_Attr & ATTR_LONG_NAME) {
        despair(); // !!!
    }
    if (DIR_entry.DIR_Attr & ATTR_VOLUME_ID) {
        equally_despair();
    }
    else if (DIR_entry.DIR_Attr & ATTR_DIRECTORY) {
        handle_direcory(); // (which might be recursive!)
    }
    else  {
        handle_file();
    }
}

Or something along those lines.

 

BTW there's an open operating system that's been doing this for years. You can read it's source code using "lxr". A Google for "lxr" should get you somewhere like:

 

https://lxr.missinglinkelectronics.com/linux

 

Digging into that a bit gets to:

 

https://lxr.missinglinkelectronics.com/linux/fs/fat/

 

For things like typedef's of MBR, boot/BPB, dir_ent and so on I wouldn't make it all up from scratch. I'd assume the Linux people have been getting this right for years and steal their definitions ;-)

For example find the file attributes here:

 

https://lxr.missinglinkelectronics.com/linux/include/uapi/linux/msdos_fs.h#L36

 

BPB is here:

 

https://lxr.missinglinkelectronics.com/linux/include/uapi/linux/msdos_fs.h#L113

 

Line 177 at:

 

https://lxr.missinglinkelectronics.com/linux/include/uapi/linux/msdos_fs.h#L177

 

has directory entry and so on (it's followed by LFN decode of the same 32 bytes)

 

Now I am not a great fan of their naming scheme - as M$ went to the trouble of giving every field in every structure an "official" name in FatGen103 then, for consistency I think everyone should use the proper names but I guess that:

struct msdos_dir_entry {
 178        __u8    name[MSDOS_NAME];/* name and extension */
 179        __u8    attr;           /* attribute bits */
 180        __u8    lcase;          /* Case for base and extension */
 181        __u8    ctime_cs;       /* Creation time, centiseconds (0-199) */
 182        __le16  ctime;          /* Creation time */
 183        __le16  cdate;          /* Creation date */
 184        __le16  adate;          /* Last access date */
 185        __le16  starthi;        /* High 16 bits of cluster in FAT32 */
 186        __le16  time,date,start;/* time, date and first cluster */
 187        __le32  size;           /* file size (in bytes) */
 188};

and

typedef struct {
    dirname_t DIR_Name;
    fattr_e DIR_Attr;
    uint8_t DIR_NTRes;
    uint8_t DIR_CrcTimeTenth;
    uint16_t DIR_CrtTime;
    uint16_t DIR_CrtDate;
    uint16_t DIR_LastAccDate;
    uint16_t DIR_FstClustHI;
    uint16_t DIR_WrtTime;
    uint16_t DIR_WrtDate;
    uint16_t DIR_FstClustLO;
    uint32_t DIR_FileSize;
} dirent_t;

have a fair amount in common. I typed my one "on the fly" as I wrote this post - the Linux one has been reading FAT disks in Linux for decades so is guaranteed "right" !

 

(in fact I already spotted the first bug in mine - can you see it ??)

 

(hint: default width of enum!)

 

Last Edited: Mon. Apr 1, 2019 - 04:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

point taken clawson, but I also have long file name entries that I need to know how to ignore?

Oh, I see the long file name is 0x0f for that bit field...

Last Edited: Mon. Apr 1, 2019 - 04:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fianawarrior wrote:
Oh, I see the long file name is 0x0f for that bit field...
Exactly that - hence "despair()" - however that could just be do_nothing().