Editing A Remote Hard Disk

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Environment: VC6, MFC, Win32 SDK

Reading and Writing Disks in Windows 95+

Many methods exist to read disks in Win 95+. I have chosen the thunking of DLLs to read the disks. This is nothing but executing the real-mode BIOS Int 13h from within the protected mode. This way, we can read the disks as if we were in real-mode DOS. However, it requires two DLLs, called the thunk DLLs. One is a 32-bit DLL that thunks down to a 16-bit DLL; it communicates with the real-mode BIOS and fetches the required buffer of data from the hard disks.

The 32-bit DLL in the protected mode translates the linear pointers and sends them down to the 16-bit DLL, which in turn calls the real-mode BIOS using the DOS Protected Mode Interface (DPMI). We have to set up a part of real-mode memory for fetching the buffer, which is mapped to the memory of the 16-bit DLL in the protected mode, which is copied to the linear memory by the routine that handles reading the buffer. For writing, the process is just the reverse; first, the buffer in the linear memory is copied to the protected mode 16-bit memory, which is mapped to the real-mode memory, which is written to the disk.

In Windows 95+, there are no restrictions on how the system is being used. The Kernel and Application levels (Ring 0 and Ring 3) are seamlessly integrated where the memories can be allocated by anything and accessed by anything.

Reading and Writing Disks in Windows NT+

In NT, the reading and writing mechanisms are entirely different. Because of the security restrictions, we cannot call the BIOS or the real-mode interrupts but only access the disks using the APIs that NT offers. The API for accessing the disks is CreateFile, which opens a disk as if it were a file; then the disk is read or written as if it were a file except that it can be done only in chunks of sectors. The disk reads and writes are treated just like files except that it does direct disk IO.

Int 13h Extensions

BIOS offers Int 13h extensions in the latest motherboards, starting a few years back. This is necessary because in the old BIOS it is not possible to read beyond 8 Gb of hard disk because of register restrictions; this is the mechanism for reading and writing disks. I have handled int 13h extensions in the thunk 16-bit DLL. Certain BIOS interrupts are added; this allows up to two-power, 64-bit number of sectors to be accessed. This is way beyond anyone’s imagination and can be used for decades without altering BIOS any more. NT internally handles int 13h extensions, so there is no need to handle the extensions separately.

Disk Organization in the FAT File System

In the FAT file system, the disk organization is straightforward and simple. The organization of the files ensures that the files are accessible through a simple walkthrough of pointers in a random fashion. This way, any file anywhere in the disk can be located with a simple mechanism. FAT12, or 12-bit FAT, is the lowest member of the FAT family, where the file pointers are just 1.5 bytes. FAT, or 16-bit FAT, is most widely used in hard disks that have 16-bits or 2 bytes per pointer. VFAT is used in Win95 on; it is the FAT with long file name support. FAT32, or 32-bit FAT, is the highest member that can handle huge hard disks as a single partition; it is used from Win95 OSR 2 on. NT 4 does not manipulate FAT32; hence, FAT is the lowest common member of the FAT file system. Starting from Win2000, the OS handles FAT32; hence, we manipulate this file system also.

The boot sector comes first in FAT, followed by hidden and reserved sectors. Then come the FAT tables. The FAT tables consist of pointers to the next cluster occupied by a file. The pointers point to the next clusters, finally terminated by a word or a double word, depending on the file system. The bad clusters are marked with a unique word or dword, thus they are skipped during the assigning process. FATs are stored in two copies for security reasons because this is the most important part of the file system. If any FAT sectors become unusable, they are skipped and the second copy is used automatically. The FAT is present in a fixed location of the disk at the start of the partition. Then, after the two copies of FAT, comes the root directory. This is a 32-byte packet with file information coded in it. The root directory is fixed in FAT12 and FAT16 but are just another sub-directory in FAT32. If they are fixed, only a certain number of files can be stored in the directory; otherwise, if, as in FAT32, they can store an enormous number of files.

Then come the files and sub-directories that are organized only with the help of the FAT tables. The starting cluster number is stored in the directory entry and it points to the next cluster in FAT table that is occupied by the file. This phenomenon continues until the end-of-file mark is reached.

Utility Program Overview to Editing Remote Hard Disks

The program for manipulating the sectors of the disk looks like the following figure. The program has two views, like Explorer. The left portion of the screen is the disks that can be edited locally; the right contains the sectors or clusters. The first pane in the status bar displays if the program is performing a calculation.

If you are connected remotely, it is displayed as “Remote – (Machine Name)”. The items expanded within it will edit the remote hard disk.

There is a server portion for the utility that is to be installed in the remote machine, which should be in the same local intranet. Once the server portion is installed in the remote machine, the local machine can communicate with it to edit the remote hard disk.

Inside Programming

The utility was developed using Visual C++ 6 and DDK. It also uses some programs such as THUNK.EXE from the Platform SDK to compile the thunk script. The thunk DLLs can be used without changing them in any programs. There is also a device driver for reading CMOS memory to know the number of floppy disks in the system. I used BC++ 3.1 to compile the 16-bit thunk DLL. The main project uses MFC and was compiled using VC++ 6.

First, I would like to give the structure of the boot record completely, including the FAT32 version of it. I give the structure of the boot record and explain its fields:

typedef struct{
  char Jmp[3];
  char OEM_Name[8];
  WORD BytesPerSector;
  unsigned char SectorsPerCluster;
  WORD ReservedSectors;
  unsigned char NumFATs;
  WORD NumRoot;
  WORD TotalSectors;
  unsigned char MediaDB;
  WORD SectorsPerFAT;
  WORD SectorsPerTrack;
  WORD NumHeads;
  DWORD NumHidSect;
  DWORD ExtendedTotalSectors;
  DWORD ExtendedSectorsPerFAT;
  WORD Flags;
  WORD Version;
  DWORD RootStartCluster;
  WORD FSInfoSec;
  WORD BkUpBootSec;
  WORD Reserved;
}BOOT;

The RootStartCluster is new to the boot record. This field contains a 32-bit starting cluster number of the root directory of the FAT32 drive. This is because the root directory is just another sub-directory in FAT32. There is also a backup boot sector in FAT32, which is denoted in BkUpBootSec.

The partition table is the first sector in a disk and contains information on the whereabouts of the disks of the system. It can have four partitions; the value of 0x80 in a field signifies the active partition. I will explain the organization of the disk after showing you the partition table:

typedef struct{
  BYTE BootInd;
  BYTE Head;
  BYTE Sector;
  BYTE Cylinder;
  BYTE SysInd;
  BYTE LastHead;
  BYTE LastSector;
  BYTE LastCylinder;
  DWORD RelativeSector;
  DWORD NumberSectors;
}PARTITION;

The BootInd at the top should be 0x80 to denote the active partition. For other partitions, it should be 0. The SysInd has the following values and meanings:

#define PART_UNKNOWN 0x00        //Unknown.
#define PART_DOS2_FAT 0x01       //12-bit FAT.
#define PART_DOS3_FAT 0x04       //16-bit FAT. Partition smaller
                                 //than 32MB.
#define PART_EXTENDED 0x05       //Extended MS-DOS Partition.
#define PART_DOS4_FAT 0x06       //16-bit FAT. Partition larger
                                 //than or equal to 32MB.
#define PART_DOS32 0x0B          //32-bit FAT. Partition up to
                                 //2047GB.
#define PART_DOS32X 0x0C         //Same as PART_DOS32(0Bh), but
                                 //uses Logical Block Address Int
                                 //13h extensions.
#define PART_DOSX13 0x0E         //Same as PART_DOS4_FAT(06h), but
                                 //uses Logical Block Address
                                 //Int 13h extensions.
#define PART_DOSX13X 0x0F        //Same as PART_EXTENDED(05h), but
                                 //uses Logical Block Address Int
                                 //13h extensions.

The NTFS currently has a value as 7, but I am not dealing with it in this article.

The Head, Sector, and Cylinder have the values of the start of the partition. Note that these are byte values; hence, for disks greater than 8 GB, these values cannot be trusted and only the RelativeSector signifies the right values. The Last values signify the end of the partition. The NumberSectors is the number of sectors for a partition belonging to this structure definition.

The partitions have been extended to handle many drives in the partition. This is accomplished by the following method:

The partition points to an Extended partition table that has the same structure but with an arrangement like linked lists. There must be one Primary partition and an extended partition which has a drive and pointer to another (extended) partition table. This setup continues till there are no more extended partitions. I have given you the deciphering of this setup in the code. The program stores the item’s data of the tree view in the memory as pointers to a structure which is described below.

typedef struct{
  WORD Drive;
  WORD Cylinder;
  WORD Head;
  WORD Sector;
  DWORD NumSectors;
  WORD Type;
  WORD FatType;
  WORD FatRelativeSector;
  DWORD NumFatSectors;
  char *Lfn;
  DWORD RelativeSector;
  WORD TotalHeads;
  DWORD StartCluster;
  WORD SectorsPerTrack;
  WORD SectorsPerCluster;
  WORD Attributes;
  DWORD TotalSectors;
  DWORD NTRelativeSector;
  DWORD DataAreaSector;
  BOOL Flag;
  BOOL Net;
}DRIVEPACKET;

A large chunk of memory is allocated during the init phase of the program. This memory is used to store all the packets of information.

The Drive is the drive number that this packet belongs to. The Cylinder, Head, and Sector point to the drive’s starting location that this packet belongs to; NumSectors is the number of sectors. Type points to one of the following values:

#define PART_TABLE 0
#define BOOT_RECORD 1
#define EXTENDED_PART 2
#define FAT16 3
#define FAT12 4
#define ROOT_DIR 5
#define FILES_FOLDERS 6
#define SECTORS_DUMP 7
#define RAW_DUMP 8
#define FAT32 9
#define SUB_DIR 10
#define UNKNOWN 11

FatType points to FAT12, FAT16, or FAT32, depending on the FAT type. FatRelativeSector points to the start of the FAT table within the partition. NumFatSectors is the number of FAT sectors. The *Lfn points to the full name of the Long File Name that this packet points to. Note that not all packets that belong to different categories have all the fields pointing to some values. For some packets, the values are significant and for others it is not. For example, in a packet pointing to PART_TABLE, the Lfn is not pointing to anything and the value is undefined. The RelativeSector points to a sector within the partition that this packet points to. This value is offset from the start of the partition pointed to by Cylinder, Head, and Sector. The NTRelativeSector points to the relative sector from the start of the disk. It is for dealing with the disks in NT/2000/XP. However, this value comes in handy when dealing with disks bigger than 8 GB, in which case the Cylinder, Head, and Sector values are neglected and this value is used throughout. The Flag is the flag that signifies whether extension to Int 0x13 is available in the system. This value is used by the 16-bit DLL to know whether NTRelativeSector is to be used or the Cylinder, Head, and Sector are to be used.

Then when we come to the coding, the notification routine which does the display of information and creating packets of drives and folders is OnItemExpanding(…) in LeftView.cpp. The first case that is handled is the PART_TABLE which is the partition table.


case PART_TABLE:{
PrevRelSector = MainPrevRelSector = 0;
hMemBufferDir = LocalAlloc(LMEM_MOVEABLE|LMEM_DISCARDABLE|
LMEM_ZEROINIT, DrivePacket->
NumSectors * 512);
if(hMemBufferDir == NULL){
AfxMessageBox(“Fatal Error! Not able to allocate a
Buffer!”);
return;
}
BufferDir = (LPBYTE) LocalLock(hMemBufferDir);
BufferNet = BufferDir;
NetEvent = BUFFER_FILL;
memcpy(&gDrivePacket, DrivePacket, sizeof(gDrivePacket));
BytesToFill = DrivePacket->NumSectors * 512;
if(DrivePacket->Net){
NetCall.Function = LOAD_SECTOR;
NetCall.Cylinder = DrivePacket->Cylinder;
NetCall.Drive = DrivePacket->Drive;
NetCall.Head = DrivePacket->Head;
NetCall.Sector = DrivePacket->Sector;
NetCall.NumSectors = DrivePacket->NumSectors;
NetCall.NTRelativeSector = DrivePacket->RelativeSector;
NetCall.RelativeSector = 0;
NetCall.Flag = DrivePacket->Flag;
send(SocketNum, (char *) &NetCall, sizeof(SECTOR_FUNC), 0);
CallNet();
}
else if(gOSWin95){
if(!(DllThunk32)(DrivePacket->Drive,
DrivePacket->Cylinder,
DrivePacket->Head,
DrivePacket->Sector,
DrivePacket->NumSectors,
(LPBYTE)BufferDir,
DrivePacket->NTRelativeSector,
DrivePacket->Flag)){
AfxMessageBox(“Partition table not accessible!”);
return;
}
}
else{
__int64 Tmp64 = ((__int64) DrivePacket->NTRelativeSector)
* 512;
long TmpVal = Tmp64 & 0xFFFFFFFF;
long TmpValHi = (Tmp64 >> 32);
SetFilePointer(hDisk[DrivePacket->Drive], TmpVal,
&TmpValHi, FILE_BEGIN);
TmpVal = 0;
ReadFile(hDisk[DrivePacket->Drive],
BufferDir, DrivePacket->NumSectors * 512,
(DWORD *) &TmpVal, NULL);
if(TmpVal != (DrivePacket->NumSectors * 512)){
AfxMessageBox(“Partition table not accessible!”);
return;
}
}
PartitionTable = (PARTITION *) (BufferDir+0x1BE);
UnknownPart = FALSE;
for(i=0; i<4; i++){
switch(PartitionTable->SysInd)
{
case 0:
continue;
break;
case PART_DOS2_FAT:
strcpy(TmpStr, “12-Bit FAT Disk”);
break;
case PART_DOS3_FAT:
strcpy(TmpStr, “16-Bit FAT Disk”);
break;
case PART_EXTENDED:
strcpy(TmpStr, “Extended DOS Partition”);
break;
case PART_DOS4_FAT:
strcpy(TmpStr, “16-Bit FAT Disk”);
break;
case PART_DOS32:
strcpy(TmpStr, “32-Bit FAT Disk”); //Normal FAT32
break;
case PART_DOS32X:
strcpy(TmpStr, “32-Bit FAT Disk”); //FAT32 with int
//13 extension

break;
case PART_DOSX13:
strcpy(TmpStr, “16-Bit FAT Disk”); //Extended int 13
break;
case PART_DOSX13X:
strcpy(TmpStr, “Extended 32-bit Partition”);
//Extended DOS
//partition in
//int 13
//extension

break;
default:
UnknownPart = TRUE;
strcpy(TmpStr, “Unknown”);
break;
}
Packets->Cylinder = PartitionTable->Cylinder;
Packets->Drive = DrivePacket->Drive;
Packets->Flag = DrivePacket->Flag;
Packets->Head = PartitionTable->Head;
Packets->Sector = PartitionTable->Sector;
Packets->NumSectors = 1;
Packets->Type = ((PartitionTable->SysInd
== PART_EXTENDED) || (PartitionTable->
SysInd == PART_DOSX13X)) ?
EXTENDED_PART:BOOT_RECORD;
if(UnknownPart)
Packets->Type = UNKNOWN;
Packets->FatType = ((PartitionTable->SysInd
== PART_DOS32) || (PartitionTable->
SysInd == PART_DOS32X)) ? FAT32:0;
if((PartitionTable->SysInd == PART_EXTENDED) ||
(PartitionTable->SysInd == PART_DOSX13X)){
MainPrevRelSector = PartitionTable->RelativeSector;
Packets->NTRelativeSector = MainPrevRelSector;
}
else{
Packets->NTRelativeSector = MainPrevRelSector
+ PartitionTable->
RelativeSector;
}
Packets->Net = DrivePacket->Net;

TvI.mask = TVIF_CHILDREN | TVIF_TEXT | TVIF_PARAM;
TvI.pszText = TmpStr;
if(UnknownPart){
Packets->RelativeSector = 1;
TvI.cChildren = 0;
}
else{
Packets->RelativeSector = 0;
TvI.cChildren = 1;
}
TvI.lParam = (long)Packets++;

TvIns.item = TvI;
TvIns.hParent = (HTREEITEM) pNMTreeView->itemNew.hItem;
TvIns.hInsertAfter = hPrev;
hPrev = DiskTree->InsertItem((LPTVINSERTSTRUCT)
&TvIns);
PartitionTable++;
}
LocalUnlock(hMemBufferDir);
LocalFree(hMemBufferDir);
break;
}

The following routine is responsible for connecting to and listening for the client machine.


BOOL ConnectAndListen(void)
{
CSADDR_INFO CSABuf[10];
DWORD dwCSABufsize = sizeof(CSABuf);
GUID guidDNS = SVCID_TCP(1026);
int lpTCP[10] = {IPPROTO_TCP, 0};
BYTE Buffer[1000];
DWORD Bytes;
PPROTOCOL_INFO lpTCPINFO;
// And create the socket descriptor
lpTCPINFO = (PPROTOCOL_INFO) Buffer;
Bytes = sizeof(Buffer);
EnumProtocols((LPINT) lpTCP, lpTCPINFO, &Bytes);
if ((SocketNum = socket(lpTCPINFO->iAddressFamily,
lpTCPINFO->iSocketType,
lpTCPINFO->iProtocol))
== SOCKET_ERROR)
{
// ERROR
return FALSE; // This will reuse the current
// ServSocks structure

}
GetAddressByName(0, // Since GUID is name space specific,
// we don’t need to specify

&guidDNS, // GUID defined by TCP port number
“UndeleteNet”, // This parameter is actually not
// used for RES_SERVICE calls

NULL, // GUID implies protocol so no need
// to specify

RES_SERVICE, // Specifies that we are trying to
// find local address to bind to

NULL, // Currently not supported
CSABuf, // Results buffer
&dwCSABufsize, // Size of results buffer
NULL, // Not supported
NULL); // Not supported
// Need at least one address
// returned in order to bind()

GetLastError();
if(bind(SocketNum, CSABuf[0].LocalAddr.lpSockaddr,
CSABuf[0].LocalAddr.iSockaddrLength)
==SOCKET_ERROR){
closesocket(SocketNum);
return FALSE;
}
if (getsockname(SocketNum,
(PSOCKADDR) &CSABuf[0].LocalAddr.lpSockaddr,
(PINT) &CSABuf[0].LocalAddr.iSockaddrLength)
== SOCKET_ERROR){
// Error — better clean up
closesocket(SocketNum);
return FALSE;
}
// Listen on the socket
if (listen(SocketNum, 5) == SOCKET_ERROR)
{
// Error — clean up
MessageBox(hDlg, “Error in listen”, “LISTEN”, MB_OK);
closesocket(SocketNum);
return FALSE;
}
if(WSAAsyncSelect(SocketNum,
hDlg,
MW_CONNECTED,
FD_ACCEPT) == SOCKET_ERROR)
{
// Error — clean up
MessageBox(hDlg, “Error in WSAAsyncSelect”, “ERROR”, MB_OK);
closesocket(SocketNum);
return FALSE;
}
return TRUE;

}

The following routine displays one sector from the local or remote hard disk differentiated by the Net flag in the DRIVEPACKET structure. If it is set to TRUE, the packet belongs to a remote machine. The remote machine will respond within a time by sending the packets of information from the server portion of the utility. The server will load the sector from the remote machine sent as a Net packet having the following structure:


typedef struct{
BYTE Function;
WORD Drive;
WORD Cylinder;
WORD Head;
WORD Sector;
DWORD NumSectors;
DWORD NTRelativeSector;
DWORD RelativeSector;
BOOL Flag;
}SECTOR_FUNC, *PSECTOR_FUNC;

The above is as a single sector packet structure as understood by the server. For multiple sectors, it is sent as the following packet to the server.


typedef struct{
BYTE Function;
DRIVEPACKET DrivePacket;
}SECTORS_FUNC, *PSECTORS_FUNC;

typedef struct{
WORD NumFloppies;
WORD NumHards;
BOOL Int13Extension;
}NUM_DISKS;

void DisplayOneSector(DRIVEPACKET *DrivePacket)
{
DRIVEPACKET TmpDrivePacket;
char TmpStr[100];
memcpy(&TmpDrivePacket, DrivePacket, sizeof(DRIVEPACKET));
TmpDrivePacket.NumSectors = 1;
if(TmpDrivePacket.RelativeSector == 0){
BufferNet = Buffer;
NetEvent = BUFFER_FILL;
memcpy(&gDrivePacket, DrivePacket, sizeof(gDrivePacket));
BytesToFill = DrivePacket->NumSectors * 512;
if(DrivePacket->Net){
NetCall.Function = LOAD_SECTOR;
NetCall.Cylinder = DrivePacket->Cylinder;
NetCall.Drive = DrivePacket->Drive;
NetCall.Head = DrivePacket->Head;
NetCall.Sector = DrivePacket->Sector;
NetCall.NumSectors = DrivePacket->NumSectors;
NetCall.NTRelativeSector = DrivePacket->NTRelativeSector;
NetCall.RelativeSector = DrivePacket->RelativeSector;
NetCall.Flag = DrivePacket->Flag;
send(SocketNum, (char *) &NetCall, sizeof(SECTOR_FUNC),
0);
CallNet();
}
else if(gOSWin95){
if(!(DllThunk32)(TmpDrivePacket.Drive,
TmpDrivePacket.Cylinder,
TmpDrivePacket.Head,
TmpDrivePacket.Sector,
TmpDrivePacket.NumSectors,
(LPBYTE)Buffer,
TmpDrivePacket.NTRelativeSector,
TmpDrivePacket.Flag)){
AfxMessageBox(“Sector not accessible!”);
return;
}
}
else{
__int64 Tmp64 = ((__int64) DrivePacket->NTRelativeSector)
* 512;
long TmpVal = Tmp64 & 0xFFFFFFFF;
long TmpValHi = (Tmp64 >> 32);
SetFilePointer(hDisk[DrivePacket->Drive], TmpVal,
&TmpValHi,
FILE_BEGIN);
TmpVal = 0;
ReadFile(hDisk[DrivePacket->Drive], Buffer,
DrivePacket->NumSectors * 512, (DWORD *)
&TmpVal, NULL);
if((!TmpVal) && (DrivePacket->Drive < 0x80)){
CloseHandle(hDisk[DrivePacket->Drive]);
char TmpStr[100];
wsprintf(TmpStr, “\\\\.\\%c:”, DrivePacket->Drive+’A’);
if((hDisk[DrivePacket->Drive]=CreateFile(
TmpStr, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING,
NULL))==INVALID_HANDLE_VALUE){
return;
}
Tmp64 = (((__int64) DrivePacket->NTRelativeSector)
+ ((__int64) DrivePacket->RelativeSector)) * 512;
TmpVal = Tmp64 & 0xFFFFFFFF;
TmpValHi = (Tmp64 >> 32);
SetFilePointer(hDisk[DrivePacket->Drive], TmpVal,
&TmpValHi,
FILE_BEGIN);
TmpVal = 0;
ReadFile(hDisk[DrivePacket->Drive], Buffer,
DrivePacket->NumSectors * 512, (DWORD *)
&TmpVal, NULL);
}
if(TmpVal != (DrivePacket->NumSectors * 512)){
AfxMessageBox(“Boot Record not accessible!”);
return;
}
}
}
else{
TmpDrivePacket.RelativeSector += gCurrentIndex;
if(!LoadSectors(&TmpDrivePacket, &CylHeadSect, Buffer)){
AfxMessageBox(“Sector not accessible!”);
return;
}
}
for(int i=0; i<32; i++){
ListView->InsertItem(LVIF_TEXT|LVIF_PARAM, i,
LPSTR_TEXTCALLBACK, 0, 0, i,
(LPARAM) i);
}
wsprintf(TmpStr, “Current Sector Number: %lu”,
TmpDrivePacket.RelativeSector);
m_wndStatusBarCtrl->SetText(TmpStr, 1, 0);
}

The connection callback gets the following message that handles various messages for read requests. LOAD_SECTORS will load many sectors and LOAD_SECTOR will load just one sector.


case MW_CONNECTED:
switch(WSAGETSELECTEVENT(lParam)){
case FD_READ:
recv(SocketNum, (char *)Function, 1000, 0);
switch(Function[0]){
case LOAD_SECTORS:
pSectorsFunction = (PSECTORS_FUNC) Function;
if(!AllocateBuffer(pSectorsFunction->
DrivePacket.NumSectors)){
MessageBox(hDlg, “Memory allocation error!”,
“SERVER”, MB_OK);
return FALSE;
}
if(!LoadSectors(&pSectorsFunction->DrivePacket,
Buffer)){
MessageBox(hDlg, “LoadSectors Error!”, “SERVER”,
MB_OK);
}
while(SentBytes < pSectorsFunction->
DrivePacket.NumSectors*512){
SentBytes += send(SocketNum, (char *)
(Buffer+SentBytes),
pSectorsFunction->
DrivePacket.NumSectors*512, 0);
}
break;
case WRITE_SECTORS:
break;
case WRITE_SECTOR:
break;
case LOAD_SECTOR:
pSectorFunction = (PSECTOR_FUNC) Function;
if(!AllocateBuffer(pSectorFunction->NumSectors)){
MessageBox(hDlg, “Memory allocation error, Sector!”,
“SERVER”, MB_OK);
return FALSE;
}
if(gOSWin95){
if(!(DllThunk32)(pSectorFunction->Drive,
pSectorFunction->Cylinder,
pSectorFunction->Head,
pSectorFunction->Sector,
pSectorFunction->NumSectors,
(LPBYTE)Buffer, pSectorFunction->
NTRelativeSector,
pSectorFunction->Flag)){
MessageBox(hDlg, “LoadSectors Error!”, “SERVER”,
MB_OK);
}
}
else{
DRIVEPACKET TmpDP, TmpDrivePacket;
memcpy(&TmpDP, pSectorFunction, sizeof(DRIVEPACKET));
__int64 Tmp64 = (((__int64) pSectorFunction->
NTRelativeSector)
+ ((__int64) pSectorFunction->
RelativeSector)) * 512;
long TmpVal = Tmp64 & 0xFFFFFFFF;
long TmpValHi = (Tmp64 >> 32);
SetFilePointer(hDisk[pSectorFunction->Drive], TmpVal,
&TmpValHi, FILE_BEGIN);
TmpVal = 0;
ReadFile(hDisk[pSectorFunction->Drive], Buffer,
pSectorFunction->NumSectors * 512,
(DWORD *) &TmpVal, NULL);
if((!TmpVal) && (pSectorFunction->Drive <
0x80)){
CloseHandle(hDisk[pSectorFunction->Drive]);
char TmpStr[100];
wsprintf(TmpStr, “\\\\.\\%c:”, pSectorFunction->
Drive+’A’);
if((hDisk[pSectorFunction->Drive]
=CreateFile(TmpStr,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING,
NULL))==INVALID_HANDLE_VALUE){
return FALSE;
}
Tmp64 = (((__int64) pSectorFunction->
NTRelativeSector) + ((__int64)
pSectorFunction->RelativeSector)) * 512;
TmpVal = Tmp64 & 0xFFFFFFFF;
TmpValHi = (Tmp64 >> 32);
SetFilePointer(hDisk[pSectorFunction->Drive],
TmpVal, &TmpValHi, FILE_BEGIN);
TmpVal = 0;
ReadFile(hDisk[pSectorFunction->Drive], Buffer,
pSectorFunction->NumSectors * 512,
(DWORD *) &TmpVal, NULL);
}
if(TmpVal != (pSectorFunction->NumSectors * 512)){
DRIVEPACKET TmpDrivePacket;
if (pSectorFunction->NumSectors == 1)
return FALSE;
memcpy(&TmpDrivePacket, &TmpDP,
sizeof(DRIVEPACKET));
for(int i=0; i<pSectorFunction->NumSectors; i++){
TmpDrivePacket.NumSectors = 1;
if(!LoadSectors(&TmpDrivePacket, (LPBYTE)
(Buffer+i*512))){
for(int j=0; j<512; j++){
Buffer[i*512+j] = 0xE5;
}
}
TmpDrivePacket.RelativeSector++;
}
break;
}
}
while(SentBytes < pSectorFunction->NumSectors*512){
SentBytes += send(SocketNum, (char *) Buffer+SentBytes,
pSectorFunction->
NumSectors*512-SentBytes, 0);
}
break;
case SAY_NUM_DISKS:
NUM_DISKS NumDisks;
if(gOSWin95)
NumDisks.Int13Extension = (DllCheckInt13Extension)
(0x80);
else
NumDisks.Int13Extension = 0;
NumDisks.NumFloppies = NumFloppies;
NumDisks.NumHards = NumHards;
send(SocketNum, (char *) &NumDisks,
sizeof(NUM_DISKS), 0);
break;
default:
MessageBox(hDlg, “Function unavailable”, “ERROR”,
MB_OK);
break;
}
break;
case FD_WRITE:
break;
case FD_ACCEPT:
SocketNum = accept(SocketNum, NULL, NULL);
if(WSAAsyncSelect(SocketNum,
hDlg,
MW_CONNECTED,
FD_READ|FD_WRITE) == SOCKET_ERROR){
// Error — clean up
MessageBox(hDlg, “WSAAsyncSelect Error”, “ACCEPT”,
MB_OK);
closesocket(SocketNum);
return FALSE;
}
break;
}

The WSAAsyncSelect socket API is used for asynchronous packets handling. The preceding routine is responsible for the callback received when packets are requested from the client machine. The server portion responds to the client by gathering the packets of information using the LoadSectors and other routines in it.

Downloads


Download source – 53 Kb


Download Server and RemoteEdit Executibles – 198 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read