The Checksum

25 Mar

The Checksum
A checksum field matches an LFN entry with its 8.3 entry. The LFNChecksum function shows how to compute the checksum. The function accepts an 11-character 8.3 file name (without the dot) and returns the checksum.

int LFNChecksum(char * ShortFileName){
int Bit7;
int Character;
int Checksum = 0;
// Step through the 11 characters in the short file name.
for (Character = 0; Character < 11; ++Character){
// Save bit 0’s value in Bit7.
if (1 & Checksum) {
Bit7 = 0×80; }
else {
Bit7 = 0×0; }
// Shift the checksum right.
Checksum = Checksum >> 1;
// Add bit 7 to the result.
Checksum = Checksum | Bit7;
// Add the next character in the file name.
Checksum += ShortFileName[Character];
// Truncate the result to 8 bits.
Checksum = Checksum & 0xFF;
}
// The result after stepping through all 11 characters is the checksum.
return(Checksum);
}

Creating a Short File Name
To convert a long file name to a short file name to store in an 8.3 entry, follow these steps:
1. Delete any spaces.
2. Delete any dots except the last dot before the extension, if present.
3. Truncate the name portion (before the dot) to six characters.
4. If there is an extension, truncate it to three characters.
5. Convert all characters from Unicode to ANSI or another 8-bit character set.
6. Convert all characters to upper case.
7. Convert any illegal 8.3 characters to underscores (_).
8. The seventh character is a tilde (~).
9. Set the eighth character to the lowest number that results in a unique file name, beginning with 1.

For example, the long file name johnsmith.txt becomes JOHNSM~1.TXT, and the long file name johnsmythe.txt in the same directory becomes JOHNSM~2.TXT. If the number following the ~ is greater than 9, the name preceding the ~ must be truncated further to enable creating a unique short file name. When five or more names have the same initial characters and extensions, Windows XP Professional uses a slightly different method to create the short file names.

Using Directories
Device firmware that supports a file system must be able to read information from directory entries and create and update directory entries when a file is created or written to. The code that follows performs these functions.

Storing an Entry
A DIRENTRY structure can store a directory entry’s 32 bytes:
#define DIR_NAMESIZE 8
#define DIR_EXTENSION 3
#define NULL 0
#define FALSE 0
#define TRUE !FALSE

typedef struct __DIRENTRY
{
char DIR_Name[DIR_NAMESIZE]; // name
char DIR_Extension[DIR_EXTENSION]; // extension
byte DIR_Attr; // attributes
byte DIR_NTRes; // reserved by NT
byte DIR_CrtTimeTenth; // time created, tenths of second portion
word DIR_CrtTime; // time created
word DIR_CrtDate; // date created
word DIR_LstAccDate; // last access date
word DIR_FstClusHI; // high word of entry’s first cluster number
word DIR_WrtTime; // last update time
word DIR_WrtDate; // last update date
word DIR_FstClusLO; // low word of entry’s first cluster number
dword DIR_FileSize; // file size
}_DIRENTRY;
typedef _DIRENTRY * DIRENTRY;

Reading an Entry
The Cache_File_Entry function returns a DIRENTRY structure containing the 32 bytes of a directory entry. The function accepts a pointer to a FILE structure (fo), a pointer to the number of the entry within its directory (curEntry), and a value (ForceRead) that helps the code decide whether to read a sector from the media or use the data in the passed FILE structure’s dsk -> buffer member.

If ForceRead is true, the function retrieves a sector from the storage media. If ForceRead is false, the function reads a sector from the storage media only if an entry is the first one in a sector. Otherwise the function uses the data in the passed buffer. Firmware can thus call the function repeatedly to retrieve a directory’s entries in sequence while reading from the media only when beginning a new sector. The FindEmptyEntries function in Chapter 10 uses the ForceRead parameter in this way.

In the passed file structure, the dirclus member must contain the number of the first cluster of the entry’s directory. If ForceRead is false, the dirccls member must contain a directory-cluster number where the code should begin looking for the entry. To begin looking at the beginning of the directory, dirccls should equal dirclus.

This function is the most complicated one in this book. Examine the comments carefully to understand it.

// A 512-byte sector can hold sixteen 32-byte directory entries.
#define DIRENTRIES_PER_SECTOR 0×10
DIRENTRY Cache_File_Entry( FILEOBJ fo, word * curEntry, byte ForceRead)
{
word ccls;
word cluster;
DIRENTRY dir;
DISK *dsk;
byte numofclus;
byte offset2;
dword sector;
// Save the file structure’s DISK member.
dsk = fo -> dsk;
// Save the number of the first cluster of the file’s directory.
cluster = fo -> dirclus;
// Save the number of the directory cluster to begin looking for the file in.
// This value is unused if ForceRead is true.
ccls = fo -> dirccls;
// Get the number of the entry’s sector within the directory.
// A sector can hold 16 directory entries. Shift right 4 times to get the entry number.
// For example, if curEntry < 10h, it’s the directory’s first sector and offset2 = 0.
// If curEntry >= 10h and < 20h, it’s the directory’s second sector and offset2 = 1.
offset2 = (*curEntry >> 4);
offset2 = offset2; // emulator issue
if (cluster != 0)

// It’s not the root directory.
// To get the number of the entry’s sector within its cluster,
// divide the sector number obtained above by the number of sectors per cluster.
// The remainder (offset2) is the sector’s number within its cluster.

// (The first sector is sector 0.)
offset2 = offset2 % (dsk -> SecPerClus);
if (ForceRead || (*curEntry & 0xf) == 0)
{
// ForceRead is true OR the entry is the first one in a sector ((*curEntry & 0xf) == 0).
// If either Condition 1 or Condition 2 below is true,
// don’t assume that ccls is the cluster to begin looking in for the entry to read.
// Instead, read the entry’s cluster number from the FAT:
// Condition 1: ForceRead is true.
// Condition 2: the entry IS in a cluster’s first sector (offset2 = 0)
// AND the entry ISN’T in the directory’s first cluster (*curEntry > 16).
if ((offset2 == 0 && (*curEntry) > DIRENTRIES_PER_SECTOR) || ForceRead)
{
if (cluster == 0)
{
// It’s the root directory. The current cluster = 0.
ccls = 0;
}
else
{

// It’s not the root directory.
if (ForceRead)
// Get the number of curEntry’s cluster within its directory:
// (curEntry / directory entries per cluster)
// directory entries per cluster =
// ((directory entries / sector) * (sectors / cluster))
numofclus =
((word)(*curEntry) /
(word)(((word)DIRENTRIES_PER_SECTOR)
* (word)dsk -> SecPerClus));

else
// The entry is in a cluster’s first sector
// AND the entry’s cluster isn’t the first one in the directory
// AND it’s not the root directory.
// Get the next cluster number.
numofclus = 1;
// To find the cluster containing curEntry,
// get the directory’s cluster numbers from the FAT until reaching the
// cluster specified by numofclus or the directory’s last cluster.
// On entering the loop, ccls = the passed dsk -> dircclus member.
while (numofclus)
{
// Read the next cluster number from the current cluster’s FAT entry.
ccls = FATread(dsk, ccls);
if (ccls >= LAST_CLUSTER)
// There is no next cluster.
break;
else
numofclus–;
}
}
} // End: read a cluster number from the FAT.

// We have a cluster number for the entry, either retrieved from the FAT
// or obtained from the passed FILE structure.
// If ccls is an EOC marker (LAST_CLUSTER code),
// the directory doesn’t have as many clusters as we thought. We can’t get the entry.
if (ccls < LAST_CLUSTER)
{
// The current cluster isn’t the last one in the file.
// We need to read a sector from the media.
// Store the cluster number in the FILE structure.
fo -> dirccls = ccls;

// Get the LBA of the cluster’s first sector.
sector = Cluster2Sector (dsk, ccls);
// If it’s the root directory (cluster 0), be sure that curEntry’s sector isn’t
// at or beyond the start of the volume’s data area.
// (curEntry’s sector = the cluster’s initial sector (sector) +
// the number of the sector in the cluster containing curEntry (offset2))
if (ccls == 0 && (sector + offset2) >= dsk->data)
{
dir = ((DIRENTRY)NULL);
}
else
{
// The sector is in a valid location
// (either the root-directory area or the volume’s data area).
// Read the data in the sector containing curEntry.
// sector = the cluster’s first sector.
// offset2 = the number of the sector within the cluster.
if ( SectorRead( sector + offset2, dsk->buffer) != sdcValid)
dir = ((DIRENTRY)NULL);
else
{

// The sector read was successful.
// Get the requested entry.
if (ForceRead)
// The directory entry is in the DISK structure’s buffer member.
// ((*curEntry) % DIRENTRIES_PER_SECTOR) =
// the number of the entry within the sector.
dir = (DIRENTRY)((DIRENTRY)dsk -> buffer)
+ ((*curEntry) % DIRENTRIES_PER_SECTOR);

else
// ForceRead is false, so the entry is the first one in the DISK
// structure’s buffer member.
// (from the if (ForceRead | (*curEntry & 0xf) == 0 ) test above)
dir = (DIRENTRY)dsk -> buffer;
}
} // End: read an entry from the media
} // End: a valid cluster was found
else
// The cluster number wasn’t valid.
dir = ((DIRENTRY)NULL);
}
else
// ForceRead is false AND curEntry isn’t the first entry in the sector.
// OK to read the directory entry directly from the passed DISK structure’s buffer.
// (No need to read a sector from the storage media.)
// ((*curEntry) % DIRENTRIES_PER_SECTOR) =
// the number of the entry within the sector.
dir =
(DIRENTRY)((DIRENTRY)dsk -> buffer) +
((*curEntry) % DIRENTRIES_PER_SECTOR);
return(dir);
}

Random Posts

No comments yet

Leave a Reply

You must be logged in to post a comment.