429 lines
15 KiB
C++
429 lines
15 KiB
C++
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// usbdevice.cpp - Access Fat16 USB volumes from SINGLDR
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
#include "usbdevice.h"
|
|
#include "fnames.h"
|
|
|
|
#pragma warning(disable: 4505) // Compiler generated constructor unreferenced
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Private functions for reading the USB disk
|
|
|
|
// read the FAT to find the cluster that follows currentCluster
|
|
uint32 UsbDevice::CalcNextCluster(uint32 currentCluster) __far
|
|
{
|
|
uint32 SectorToRead, OffsetInSector;
|
|
uint32 result32;
|
|
uint16 result16;
|
|
|
|
OffsetInSector = (currentCluster*FatOffsetMultiplier) % (BytesPerSec);
|
|
SectorToRead = ((currentCluster*FatOffsetMultiplier) / (BytesPerSec)) + (RsvdSecs);
|
|
|
|
uint8 __far * buffer = FatBuffer;
|
|
|
|
BiosDiskReadCHS(buffer, SectorToRead, BootDrive, 1, SecsPerTrack, NumHeads);
|
|
|
|
result16 = *((uint16 __far *)(buffer+OffsetInSector));
|
|
result32 = result16; // implicit cast
|
|
return result32;
|
|
}
|
|
|
|
// read cluster # ClusterNum into a (pre-allocated) buffer
|
|
// due to CHS limitations, we have to read it one sector at a time...
|
|
void UsbDevice::ReadCluster(uint32 ClusterNum, uint8 __far * buffer) __far
|
|
{
|
|
uint8 __far * buff = buffer;
|
|
int sectorcounter;
|
|
|
|
// figure out the first sector:
|
|
uint32 startingSector = ((ClusterNum - 2) * (SecsPerClus)) + (FirstDataSec);
|
|
|
|
for (sectorcounter = 0; sectorcounter < SecsPerClus; sectorcounter++) {
|
|
// read one sector
|
|
BiosDiskReadCHS(buff, startingSector, BootDrive, 1, SecsPerTrack, NumHeads);
|
|
|
|
// advance to next sector
|
|
startingSector++;
|
|
|
|
// advance the buffer
|
|
buff += BytesPerSec;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Private Method for matching filenames
|
|
// (uses public funcs from FatDevice code)
|
|
|
|
// Search from the first cluster of a directory entry to find the Fat
|
|
// encoding of a filename. We accomplish this through a FSM.
|
|
// Fat16RootDir is a special flag the root directory is not a cluster chain,
|
|
// but all other directories are.
|
|
int UsbDevice::DirLookup(LPCHAR filename,
|
|
uint8 /* len */,
|
|
FilePtr directory,
|
|
FilePtr file,
|
|
int Fat16RootDir) __far
|
|
{
|
|
// points into the data we've loaded from disk:
|
|
uint8 __far * buffer;
|
|
|
|
// we will use the generic term "block" to refer to a readable entity
|
|
// on the disk, since the Fat16 root dir is in sectors while the rest
|
|
// of Fat is in clusters
|
|
uint16 totalBlocks, blockCounter = 0;
|
|
uint16 nextBlock, entriesPerBlock, entryCounter = 0;
|
|
|
|
if (Fat16RootDir) {
|
|
totalBlocks = RootDirSecs;
|
|
entriesPerBlock = BytesPerSec / 32;
|
|
nextBlock = (uint16)RootStartSec;
|
|
}
|
|
else {
|
|
totalBlocks = 2; // value doesn't matter; make sure totalBlocks>blockCounter
|
|
entriesPerBlock = (uint16)DirEntriesPerClus;
|
|
nextBlock = (uint16)directory->FirstBlock;
|
|
}
|
|
|
|
// "segment" will refer to the portion of the LongFileName
|
|
// about which we care, since it will be in 13-char chunks
|
|
uint8 totalSegs, segCounter, segsMatched = 0;
|
|
totalSegs = FullFNameLength(filename);
|
|
totalSegs = (uint8) ((totalSegs + 12) / 13);
|
|
segCounter = totalSegs;
|
|
|
|
// long file name verification requires a checksum, as well as matching a
|
|
// special "entry signature" in byte[0] of the directory entry
|
|
uint8 checksum = 0, signature = 0;
|
|
|
|
// the primary loop fetches data to check
|
|
while (blockCounter < totalBlocks) {
|
|
// read some data and set a counter for when we need more data
|
|
if (Fat16RootDir) {
|
|
BiosDiskReadCHS(FatBuffer, nextBlock, BootDrive, 1, SecsPerTrack, NumHeads);
|
|
entryCounter = BytesPerSec / 32;
|
|
}
|
|
else {
|
|
ReadCluster(nextBlock, FatBuffer);
|
|
entryCounter = (uint16)DirEntriesPerClus;
|
|
}
|
|
buffer = FatBuffer;
|
|
|
|
// the inner loop checks entries
|
|
while (entryCounter>0) {
|
|
// failure condition #1: if the first bit is zero,
|
|
// there are no more directory entries.
|
|
if (buffer[0] == 0) {
|
|
return -1;
|
|
}
|
|
|
|
// success condition #1: do we match on 8.3?
|
|
// success condition #2: if segCounter == 0, does checksum match?
|
|
if ((FatShortNameMatch(buffer, filename) == 1) ||
|
|
(segCounter == 0 && FatChecksum(buffer) == checksum))
|
|
{
|
|
file->FirstBlock = (*((uint16 __far *)(buffer+20))<<16) +
|
|
*((uint16 __far *)(buffer+26));
|
|
file->Size = *((uint32 __far *)(buffer+28));
|
|
return 0;
|
|
}
|
|
|
|
// progress condition: do we match on the current segment of
|
|
// long file name?
|
|
// first calc checksum and the signature
|
|
if (segCounter == totalSegs) {
|
|
signature = (uint8) (segCounter | 0x40);
|
|
checksum = buffer[13];
|
|
}
|
|
else {
|
|
signature = segCounter;
|
|
}
|
|
// now do the check
|
|
if (signature == FatLongNamePartialMatch(buffer, filename, signature, checksum)) {
|
|
// advance to next directory entry and earlier segment in the filename
|
|
segCounter--;
|
|
buffer += 32;
|
|
entryCounter--;
|
|
}
|
|
else {
|
|
// if we were checking the first LFN entry then it's time
|
|
// to advance the counter
|
|
if (segCounter == totalSegs) {
|
|
buffer += 32;
|
|
entryCounter--;
|
|
}
|
|
else {
|
|
// otherwise this might just be the start of
|
|
// the correct entry, so don't advance the counter,
|
|
// just reset to the first segment of the filename
|
|
segCounter = totalSegs;
|
|
}
|
|
}
|
|
}
|
|
// calculate next data block
|
|
if (Fat16RootDir == 1) {
|
|
nextBlock++;
|
|
blockCounter++;
|
|
}
|
|
else {
|
|
nextBlock = (uint16) CalcNextCluster(nextBlock);
|
|
if (nextBlock >= (EndOfClusterMarker)) {
|
|
blockCounter = 2;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Public Functions
|
|
|
|
int UsbDevice::OpenDevice() __far
|
|
{
|
|
int entrycounter = 4; // up to 4 entries in partition table
|
|
|
|
// allocate memory for reading a sector (assume 512-byte sector size)
|
|
BootSectorBuffer = (uint8 __far *) alloc(512, 0);
|
|
|
|
// Read the BootSector
|
|
// we are in a bind here: we don't know the SecsPerTrack or the NumHeads
|
|
// but we need them to do the read!
|
|
BiosDiskReadCHS(BootSectorBuffer, 0, BootDrive, 1, 1, 1);
|
|
|
|
// temp to make using far pointers easier
|
|
uint8 __far * buffer = BootSectorBuffer;
|
|
|
|
// read data that should always be in the same place, no matter what
|
|
BytesPerSec = *((uint16 __far *)(buffer+11));
|
|
SecsPerClus = buffer[13];
|
|
SecsPerTrack = *((uint16 __far *)(buffer+24));
|
|
NumHeads = *((uint16 __far *)(buffer+26));
|
|
RsvdSecs = *((uint16 __far *)(buffer+14));
|
|
NumFats = buffer[16];
|
|
RootDirEntries = *((uint16 __far *)(buffer+17));
|
|
HiddenSecs = *((uint32 __far *)(buffer+28));
|
|
|
|
// "compute" the NumFatSecs and TotalSecs fields
|
|
// (i.e. read one value, if it is zero, read another)
|
|
uint16 tmp16;
|
|
tmp16 = *((uint16 __far *)(buffer+19));
|
|
if (tmp16 == 0) {
|
|
TotalSecs = *((uint32 __far *)(buffer+32));
|
|
}
|
|
else {
|
|
TotalSecs = tmp16;
|
|
}
|
|
|
|
tmp16 = *((uint16 __far *)(buffer+22));
|
|
if (tmp16 == 0) {
|
|
NumFatSecs = *((uint32 __far *)(buffer+36));
|
|
}
|
|
else {
|
|
NumFatSecs = tmp16;
|
|
}
|
|
|
|
// now we may compute RootDirSectors =
|
|
// ((BPB_RootEntCnt*32) + (BPB_BytsPerSec-1))/BPB_BytesPerSec
|
|
RootDirSecs = ((RootDirEntries*32) + (BytesPerSec-1))/BytesPerSec;
|
|
|
|
// Next compute the total data sectors in the volume:
|
|
TotalDataSecs = TotalSecs - (RsvdSecs + (NumFats*NumFatSecs) + RootDirSecs);
|
|
|
|
// Finally, compute the # of clusters in the volume:
|
|
ClusterCount = TotalDataSecs / SecsPerClus;
|
|
|
|
// Do the verification:
|
|
if (!(ClusterCount >= 4085 && ClusterCount<65525)) {
|
|
printf("USB: File System Type is not supported\n");
|
|
return -1; // the partition table did not match the boot sector
|
|
// we can't trust this filesystem.
|
|
}
|
|
FatType = 16;
|
|
|
|
// now we can set fields that are FAT-type specific:
|
|
EndOfClusterMarker = 0xFFF8;
|
|
BadClusterMarker = 0xFFF7;
|
|
FatOffsetMultiplier = 2;
|
|
RootStartSec = (RsvdSecs + (NumFats*NumFatSecs));
|
|
|
|
// final computations:
|
|
FirstDataSec = (NumFats*NumFatSecs) + RsvdSecs + RootDirSecs;
|
|
BytesPerClus = SecsPerClus * BytesPerSec;
|
|
DirEntriesPerClus = BytesPerClus/32;
|
|
|
|
// allocate data for remaining fs buffers:
|
|
FatBuffer = (uint8 __far *) alloc((uint16)BytesPerClus, 0); // exactly the size of a cluster
|
|
FileBuffer = (uint8 __far *) alloc(0x7FFF, 0); // 32KB
|
|
|
|
// display results
|
|
#if 0
|
|
printf("\nUsb Configuration\n");
|
|
printf("------------------------------\n");
|
|
printf("Boot Drive = %2xh\n", BootDrive);
|
|
printf("Bytes/Sector = %4xh\n", BytesPerSec);
|
|
printf("\nFileSystem Configuration\n");
|
|
printf("------------------------------\n");
|
|
printf("Fat Type = FAT%d\n", FatType);
|
|
printf("Sectors/Cluster = %2xh\n", SecsPerClus);
|
|
printf("Reserved Sectors = %4xh\n", RsvdSecs);
|
|
printf("Number of Fats = %2xh\n", NumFats);
|
|
printf("Hidden Sectors = %8lxh\n", HiddenSecs);
|
|
printf("Sectors per Fat = %8lxh\n", NumFatSecs);
|
|
printf("First Data Sector in Partition = %8lxh\n", FirstDataSec);
|
|
printf("Bytes/Cluster = %8lxh\n", BytesPerClus);
|
|
printf("Dir Entries/Cluster = %8lxh\n", DirEntriesPerClus);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int UsbDevice::CloseDevice() __far
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int UsbDevice::GetFileProperties(LPCHAR filename,
|
|
FilePtr file,
|
|
FilePtr directory) __far
|
|
{
|
|
LPCHAR fname = filename;
|
|
uint8 len;
|
|
int result;
|
|
char c;
|
|
int isRootDir = 1; // the Fat16 Root Directory is special.
|
|
// Flag the first Fat16 read (but not subsequent reads)
|
|
|
|
// we'll continually recycle the File and Directory structs
|
|
file->Size = 0;
|
|
file->FirstBlock = 0;
|
|
|
|
// traverse through the filename, identifying tokens and looking them
|
|
// up in the current context
|
|
bool done = 0;
|
|
while (!done) {
|
|
// consume the leading '/'
|
|
while (fname[0] == '/') {
|
|
fname++;
|
|
}
|
|
|
|
// failure condition: whitespace
|
|
if (IsEndToken(fname[0])) {
|
|
printf("USB: Invalid filename: ");
|
|
PutFName(filename);
|
|
printf("\n");
|
|
return -1;
|
|
}
|
|
|
|
// find the next separator in the filename ('/' or whitespace),
|
|
// store it, and replace it with 0
|
|
len = ShortFNameLength(fname);
|
|
c = fname[len];
|
|
fname[len] = 0;
|
|
|
|
// look it up and put the results into File
|
|
result = DirLookup(fname, len, directory, file, isRootDir);
|
|
|
|
// undo the change we made to the filename:
|
|
fname[len] = c;
|
|
|
|
// now shut off the isRootDir flag for subsequent directory scans
|
|
isRootDir = 0;
|
|
if (result == -1) {
|
|
printf("USB: File not found: ");
|
|
PutFName(filename);
|
|
printf("\n");
|
|
return -1;
|
|
}
|
|
|
|
// check loop termination condition
|
|
if (c != '/') {
|
|
done = 1;
|
|
}
|
|
else {
|
|
fname += len;
|
|
// transfer results from File into Directory
|
|
directory->FirstBlock = file->FirstBlock;
|
|
directory->Size = file->Size;
|
|
file->Size = 0;
|
|
file->FirstBlock = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
INT16 UsbDevice::ReadFileLow(LPCHAR /* filename */,
|
|
FilePtr file,
|
|
uint8 __far * buffer) __far
|
|
{
|
|
uint32 bytesread = 0;
|
|
uint8 __far * destination = buffer;
|
|
uint32 currentcluster = file->FirstBlock;
|
|
|
|
while (bytesread < file->Size) {
|
|
// read the current cluster
|
|
if (currentcluster == BadClusterMarker) {
|
|
printf("USB: Bad Cluster encountered\n");
|
|
return -1;
|
|
}
|
|
ReadCluster(currentcluster, destination);
|
|
currentcluster = CalcNextCluster(currentcluster);
|
|
bytesread += BytesPerClus;
|
|
destination = (uint8 __far *) _MK_FP(_FP_SEG(destination), (_FP_OFF(destination)+BytesPerClus));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32 UsbDevice::ReadFileHigh(
|
|
LPCHAR /* filename */,
|
|
FilePtr file,
|
|
uint32 destinationAddress,
|
|
uint32 /* cbDestinationAddress */
|
|
) __far
|
|
{
|
|
uint32 bytesread = 0;
|
|
uint32 nextdestination = destinationAddress;
|
|
uint32 currentcluster = file->FirstBlock;
|
|
uint32 sector;
|
|
|
|
uint32 bufferaddress = PointerToUint32(FileBuffer);
|
|
|
|
while (bytesread < file->Size) {
|
|
// ensure cluster is valid
|
|
if (currentcluster == BadClusterMarker) {
|
|
printf("USB: Bad Cluster encountered\n");
|
|
return bytesread;
|
|
}
|
|
|
|
// calc true sector for this cluster
|
|
sector = ((currentcluster-2) * SecsPerClus)+FirstDataSec;
|
|
|
|
// read one cluster
|
|
BiosDiskReadCHS(FileBuffer, sector, BootDrive, SecsPerClus, SecsPerTrack, NumHeads);
|
|
|
|
// move the data into extended memory
|
|
PModeTransfer(bufferaddress, nextdestination, BytesPerClus);
|
|
|
|
// get next cluster number
|
|
currentcluster = CalcNextCluster(currentcluster);
|
|
|
|
// update the count of bytes read
|
|
bytesread += BytesPerClus;
|
|
|
|
// and update the destination address
|
|
nextdestination += BytesPerClus;
|
|
}
|
|
|
|
// since we read full sectors at the bios level, we should
|
|
// trim our count back down a bit here
|
|
if (bytesread>file->Size) {
|
|
bytesread = file->Size;
|
|
}
|
|
return bytesread;
|
|
}
|