MFMT.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/*++

Module Name:

mfmt.c

Abstract:

This program is designed to show how to access a physical floppy
disk using the Win32 API set.

This program has two major functions.

- It can be used to display the geometry of a disk

mfmt -g a:

- It can be used to produce a disk image, or to write a disk
image to a floppy.

mfmt -c a: bootdisk - produce a disk image of a:

mfmt -c bootdisk a: - make a: identical to bootdisk image

This program is very very simple. Minimal error checking is done. It is
meant to provide an example of how to:

- Open a physical disk

- Read a disk's geometry

- Perform a low level format operation

- read and write physical sectors

--*/

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <winioctl.h>
#include <string.h>
#include <ctype.h>
#include <memory.h>

DISK_GEOMETRY SupportedGeometry[20];
DWORD SupportedGeometryCount;

BOOL
GetDiskGeometry(
HANDLE hDisk,
PDISK_GEOMETRY lpGeometry
)

{
DWORD ReturnedByteCount;

return DeviceIoControl(
hDisk,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0,
lpGeometry,
sizeof(*lpGeometry),
&ReturnedByteCount,
NULL
);
}

DWORD
GetSupportedGeometrys(
HANDLE hDisk
)
{
DWORD ReturnedByteCount;
BOOL b;
DWORD NumberSupported;

b = DeviceIoControl(
hDisk,
IOCTL_DISK_GET_MEDIA_TYPES,
NULL,
0,
SupportedGeometry,
sizeof(SupportedGeometry),
&ReturnedByteCount,
NULL
);
if ( b ) {
NumberSupported = ReturnedByteCount / sizeof(DISK_GEOMETRY);
}
else {
NumberSupported = 0;
}
SupportedGeometryCount = NumberSupported;

return NumberSupported;
}

VOID
PrintGeometry(
LPSTR lpDriveName,
PDISK_GEOMETRY lpGeometry
)
{
LPSTR MediaType;

if (lpDriveName) {
printf("Geometry for Drive %s\n",lpDriveName);
}

switch ( lpGeometry->MediaType ) {
case F5_1Pt2_512: MediaType = "5.25, 1.2MB, 512 bytes/sector";break;
case F3_1Pt44_512: MediaType = "3.5, 1.44MB, 512 bytes/sector";break;
case F3_2Pt88_512: MediaType = "3.5, 2.88MB, 512 bytes/sector";break;
case F3_20Pt8_512: MediaType = "3.5, 20.8MB, 512 bytes/sector";break;
case F3_720_512: MediaType = "3.5, 720KB, 512 bytes/sector";break;
case F5_360_512: MediaType = "5.25, 360KB, 512 bytes/sector";break;
case F5_320_512: MediaType = "5.25, 320KB, 512 bytes/sector";break;
case F5_320_1024: MediaType = "5.25, 320KB, 1024 bytes/sector";break;
case F5_180_512: MediaType = "5.25, 180KB, 512 bytes/sector";break;
case F5_160_512: MediaType = "5.25, 160KB, 512 bytes/sector";break;
case RemovableMedia: MediaType = "Removable media other than floppy";break;
case FixedMedia: MediaType = "Fixed hard disk media";break;
default: MediaType = "Unknown";break;
}
printf(" Media Type %s\n",MediaType);
printf(" Cylinders %d Tracks/Cylinder %d Sectors/Track %d\n",
lpGeometry->Cylinders.LowPart,
lpGeometry->TracksPerCylinder,
lpGeometry->SectorsPerTrack
);
}

BOOL
LowLevelFormat(
HANDLE hDisk,
PDISK_GEOMETRY lpGeometry
)
{
FORMAT_PARAMETERS FormatParameters;
PBAD_TRACK_NUMBER lpBadTrack;
UINT i;
BOOL b;
DWORD ReturnedByteCount;

FormatParameters.MediaType = lpGeometry->MediaType;
FormatParameters.StartHeadNumber = 0;
FormatParameters.EndHeadNumber = lpGeometry->TracksPerCylinder - 1;
lpBadTrack = (PBAD_TRACK_NUMBER) LocalAlloc(LMEM_ZEROINIT,lpGeometry->TracksPerCylinder*sizeof(*lpBadTrack));

for (i = 0; i < lpGeometry->Cylinders.LowPart; i++) {

FormatParameters.StartCylinderNumber = i;
FormatParameters.EndCylinderNumber = i;

b = DeviceIoControl(
hDisk,
IOCTL_DISK_FORMAT_TRACKS,
&FormatParameters,
sizeof(FormatParameters),
lpBadTrack,
lpGeometry->TracksPerCylinder*sizeof(*lpBadTrack),
&ReturnedByteCount,
NULL
);

if (!b ) {
LocalFree(lpBadTrack);
return b;
}
}

LocalFree(lpBadTrack);

return TRUE;
}

BOOL
LockVolume(
HANDLE hDisk
)
{
DWORD ReturnedByteCount;

return DeviceIoControl(
hDisk,
FSCTL_LOCK_VOLUME,
NULL,
0,
NULL,
0,
&ReturnedByteCount,
NULL
);
}

BOOL
UnlockVolume(
HANDLE hDisk
)
{
DWORD ReturnedByteCount;

return DeviceIoControl(
hDisk,
FSCTL_UNLOCK_VOLUME,
NULL,
0,
NULL,
0,
&ReturnedByteCount,
NULL
);
}

BOOL
DismountVolume(
HANDLE hDisk
)
{
DWORD ReturnedByteCount;

return DeviceIoControl(
hDisk,
FSCTL_DISMOUNT_VOLUME,
NULL,
0,
NULL,
0,
&ReturnedByteCount,
NULL
);
}

DWORD
_cdecl
main(
int argc,
char *argv[],
char *envp[]
)
{
char Drive[MAX_PATH];
HANDLE hDrive, hDiskImage;
DISK_GEOMETRY Geometry;
UINT i;
char c, *p;
LPSTR DriveName;
BOOL fUsage = TRUE;
BOOL fShowGeometry = FALSE;
BOOL fDiskImage = FALSE;
BOOL SourceIsDrive;
LPSTR Source, Destination, DiskImage;

if ( argc > 1 ) {
fUsage = FALSE;
while (--argc > 0 ) {
p = *++argv;
if (*p == '/' || *p == '-') {
while (c = *++p)
switch (toupper( c )) {
case '?':
fUsage = TRUE;
break;

case 'C':
fDiskImage = TRUE;
argc--, argv++;
Source = *argv;
argc--, argv++;
Destination = *argv;
break;

case 'G':
argc--, argv++;
if ( (DriveName = *argv ) && *DriveName &&
isalpha(*DriveName) )
fShowGeometry = TRUE;
else
{
printf( "MFMT: Missing drive letter after -G\n" );
DriveName = NULL;
fUsage = TRUE;
}
break;

default:
printf("MFMT: Invalid switch - /%c\n", c );
fUsage = TRUE;
break;
}
}
}
}

if ( fUsage ) {
printf("usage: MFMT switches \n" );
printf(" [-?] display this message\n" );
printf(" [-g drive] shows disk geometry\n" );
printf(" [-c source destination] produce diskimage\n" );
ExitProcess(1);
}

if ( fShowGeometry ) {
sprintf(Drive,"\\\\.\\%s",DriveName);
hDrive = CreateFile(
Drive,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ( hDrive == INVALID_HANDLE_VALUE ) {
printf("MFMT: Open %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}

if ( LockVolume(hDrive) == FALSE ) {
printf("MFMT:Locking volume %s failed %d\n", DriveName, GetLastError());
ExitProcess(1);
}

if ( !GetDiskGeometry(hDrive,&Geometry) ) {
printf("MFMT: GetDiskGeometry %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}
PrintGeometry(DriveName,&Geometry);

if ( !GetSupportedGeometrys(hDrive) ) {
printf("MFMT: GetSupportedGeometrys %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}
printf("\nDrive %s supports the following disk geometries\n",DriveName);

for(i=0;i<SupportedGeometryCount;i++) {
printf("\n");
PrintGeometry(NULL,&SupportedGeometry[i]);
}

printf("\n");
ExitProcess(0);
}

if ( fDiskImage ) {
SourceIsDrive = FALSE;
if ( Source[strlen(Source)-1] == ':' ) {
SourceIsDrive = TRUE;
sprintf(Drive,"\\\\.\\%s",Source);
DriveName=Source;
DiskImage = Destination;
}
if ( Destination[strlen(Destination)-1] == ':' ) {
if ( SourceIsDrive ) {
printf("MFMT: Source and Destination cannot both be drives\n");
ExitProcess(1);
}
SourceIsDrive = FALSE;
sprintf(Drive,"\\\\.\\%s",Destination);
DriveName=Destination;
DiskImage = Source;
}
else {
if ( !SourceIsDrive ) {
printf("MFMT: Either Source or Destination must be a drive\n");
ExitProcess(1);
}
}

//
// Open and Lock the drive
//

hDrive = CreateFile(
Drive,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ( hDrive == INVALID_HANDLE_VALUE ) {
printf("MFMT: Open %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}
if ( LockVolume(hDrive) == FALSE ) {
printf("MFMT: LockVolume %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}

if ( !GetDiskGeometry(hDrive,&Geometry) ) {
printf("MFMT: GetDiskGeometry %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}

if ( !GetSupportedGeometrys(hDrive) ) {
printf("MFMT: GetSupportedGeometrys %s failed %d\n",DriveName,GetLastError());
ExitProcess(1);
}

//
// Open the disk image file
//

hDiskImage = CreateFile(
DiskImage,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
SourceIsDrive ? CREATE_ALWAYS : OPEN_EXISTING,
0,
NULL
);
if ( hDiskImage == INVALID_HANDLE_VALUE ) {
printf("MFMT: Open %s failed %d\n",DiskImage,GetLastError());
ExitProcess(1);
}

//
// Now do the copy
//
{
LPVOID IoBuffer;
BOOL b;
DWORD BytesRead, BytesWritten;
DWORD FileSize;
DWORD VirtBufSize;
DWORD NumBufs;

//
// If we are copying from floppy to file, just do the copy
// Otherwise, we might have to format the floppy first
//

if ( SourceIsDrive ) {

//
// Device reads must be sector aligned. VirtualAlloc will
// garuntee alignment
//

NumBufs = Geometry.Cylinders.LowPart;
VirtBufSize = Geometry.TracksPerCylinder *
Geometry.SectorsPerTrack *
Geometry.BytesPerSector;

IoBuffer = VirtualAlloc(NULL,VirtBufSize,MEM_COMMIT,PAGE_READWRITE);

if ( !IoBuffer ) {
printf("MFMT: Buffer Allocation Failed\n");
ExitProcess(1);
}

for ( ;NumBufs > 0; NumBufs-- )
{
b = ReadFile(hDrive,IoBuffer, VirtBufSize, &BytesRead, NULL);
if (b && BytesRead){
b = WriteFile(hDiskImage,IoBuffer, BytesRead, &BytesWritten, NULL);
if ( !b || ( BytesRead != BytesWritten ) ) {
printf("MFMT: Fatal Write Error %d\n",GetLastError());
ExitProcess(1);
}
}
else {
printf("MFMT: Fatal Read Error %d\n",GetLastError());
ExitProcess(1);
}
}
}
else {

//
// Check to see if the image will fit on the floppy. If it
// will, then LowLevelFormat the floppy and press on
//

FileSize = GetFileSize(hDiskImage,NULL);

b = FALSE;
for(i=0;i<SupportedGeometryCount;i++) {
NumBufs = SupportedGeometry[i].Cylinders.LowPart;
VirtBufSize = SupportedGeometry[i].TracksPerCylinder *
SupportedGeometry[i].SectorsPerTrack *
SupportedGeometry[i].BytesPerSector;
if ( VirtBufSize*NumBufs >= FileSize ) {

IoBuffer = VirtualAlloc(NULL,VirtBufSize,MEM_COMMIT,PAGE_READWRITE);

if ( !IoBuffer ) {
printf("MFMT: Buffer Allocation Failed\n");
ExitProcess(1);
}

//
// Format the floppy
//

LowLevelFormat(hDrive,&SupportedGeometry[i]);

for ( ;NumBufs > 0; NumBufs-- )
{
b = ReadFile(hDiskImage,IoBuffer, VirtBufSize, &BytesRead, NULL);
if (b && BytesRead){
b = WriteFile(hDrive,IoBuffer, BytesRead, &BytesWritten, NULL);
if ( !b || ( BytesRead != BytesWritten ) ) {
printf("MFMT: Fatal Write Error %d\n",GetLastError());
ExitProcess(1);
}
}
else {
printf("MFMT: Fatal Read Error %d\n",GetLastError());
ExitProcess(1);
}
}
b = TRUE;
break;
}
}

if ( !b ) {
printf("MFMT: FileSize %d is not supported on drive %s\n",FileSize,DriveName);
ExitProcess(1);
}
}
}

//
// Dismounting forces the filesystem to re-evaluate the media id
// and geometry. This is the same as popping the floppy in and out
// of the disk drive
//

DismountVolume(hDrive);
UnlockVolume(hDrive);

ExitProcess(0);
}
return (0);
}