/*++ Copyright (c) 1991 Microsoft Corporation Module Name: simbad.c (formerly simbad.cxx) Abstract: This module contains the main function of SimBad, which allows the user to to ask the device driver to simulate bad sectors. Author: Bill McJohn (billmc) 16-Aug-1991 Lars Opstad (a-larso) 6-Feb-1993 Environment: ULIB, User Mode Revision History: 4.Apr.92 - BobRi - Minor fix to list function (device control interface) 9.Apr.92 - BobRi - Modified (with BillMc's help) to support the access triggers on when block numbers are to fail. 29.Jan.93 - LarsOp - Major changes to change to allow redirected console input. This required lots of changes because of dependencies on cxx libraries -> convert to C. Also added functionality on Sanjay's request for supporting file input with multiple lists of bad sectors. 5.Feb.93 - Larsop - Added new function - orphan device. Any accesses to the indicated partition will fail with a status of STATUS_IO_DEVICE_ERROR. 20.May.93 - mglass - Add support for VERIFY access type. This will allow SIMBAD to test the format utiltiy. 21.Jul.93 - mglass - Define access bit to fail attempt to reassign blocks. 12.May.94 - Venkat - Added code to drop of writes to DISK (CHKDSK testing) 22.Nov.94 - kpeery - Added code to force hardware reset (for restarts) --*/ #include "stdlib.h" #include "stdio.h" #include "nt.h" #include "ntrtl.h" #include "ntdddisk.h" #include // // Constants for command line arguments // #define HD_SPEC 'h' #define PART_SPEC 'p' #define LIST 'l' #define ZERO_OFF 'z' #define MAP 'm' #define ADD_ALL 'a' #define ADD_READ 'r' #define ADD_WRITE 'w' #define ADD_VERIFY 'v' #define DO_NOT_MAP 'x' #define ENABLE 'e' #define DISABLE 'd' #define ORPHAN 'o' #define CLEAR 'c' #define FROM_FILE 'f' #define RANDOM 'n' #define BUGCHECK 'b' #define FIRMWARE_RESET 't' #define MAX_DEV_NAME 128 char devname[MAX_DEV_NAME]="\\Device\\HardDisk0\\Partition1"; char *filename; WCHAR whattodo=LIST; ULONG Seed, accessType=0; int hdnum=0, partnum=1; ULONG sectors[MAXIMUM_SIMBAD_SECTORS]; VOID InputSectorList ( OUT PULONG sectors, OUT PULONG count ) /*++ Routine Description: This function reads the sector list from stdin. The list is terminated by end of file (max bad sectors entered). Arguments: sectors -- receives array of sectors count -- receives number of sectors read from stdin Return value: None. --*/ { // // loop until end-of-file or count exceeds max sectors // while (!feof(stdin) && (*count) < MAXIMUM_SIMBAD_SECTORS) { fscanf(stdin, "%lx", &(sectors[(*count)++])); } // while // // if count is less than MAX_BAD_SECTORS, // the last read was an EOF => decrement count. // if (*count < MAXIMUM_SIMBAD_SECTORS) { (*count)--; } // if } // InputSectorList() VOID ShowUsage ( ) /*++ Routine Description: This function displays command line options for Simbad. Arguments: None. Return value: None. --*/ { printf("\nUsage: Simbad DeviceSpecifier /command /options\n" "or Simbad /f file /options\n" "\nwhere DeviceSpecifier is in one of the following formats:\n" "\t\\Device\\HardDisk#\\Partition#\n" "\t\\DosDevices\\C:\n" "\t/h /p \n" "\nand command is one of the following:\n" "\ta Fail all access (enter sector list)\n" "\tr Fail read access (enter sector list)\n" "\tw Fail write access (enter sector list)\n" "\tv Fail verify access (enter sector list)\n" "\to Orphan entire device (return DEVICE FAILURE)\n" "\tn Dropping writes randomly\n" "\tb BugChecks the system\n" "\tt Reset the system\n" "\te Enable (previously entered) bad sectors\n" "\td Disable (previously enabled) bad sectors\n" "\tl List bad sectors\n" "\tc Clear bad sectors\n" "\nand options includes any of the following:\n" "\tm Pass block reassignments to lower driver\n" "\tz Failures report zero offset\n" "\tx Fail block reassignment\n" "\nNote: /h and /p have been added. The following cmd lines are equivalent:\n\n" "\tSimbad \\Device\\HardDisk0\\Partition1 /l\n" "\tSimbad /h 0 /p 1 /l\n" "\nThe format of the file used with the /f option is as follows:\n\n" "\\Device\\HardDisk#\\Partition#\n" "R:0 8 a ...\n" "W:...\n" "A:...\n\n" "This file can contain lists for multiple devices.\n" "The bad sector emulation is not enabled until the /e option is entered.\n" ); exit(-1); } // ShowUsage(); VOID ParseCmdArgs ( IN int argc, IN char *argv[] ) /*++ Routine Description: This function sets some global variables based on the command line arguments given to main (and passed here). The globals that are set are as follows: devname -- name of device to simulate bad sectors on accessType -- fail on READ or WRITE, reassign, zero offset whattodo -- which action (add, list, enable/disable, readfromfile) filename -- which filename to use for FROM_FILE action Arguments: argc -- supplies number of arguments argv -- supplies list of arguments Return value: None. --*/ { int i=1; // // if no arguments, just show usage. // if (argc==1) { ShowUsage(); } // if // // check each argument // while (i MAXIMUM_SIMBAD_SECTORS ) { return FALSE; } // if SimbadData.Function = Add ? SIMBAD_ADD_SECTORS : SIMBAD_REMOVE_SECTORS; SimbadData.Count = Count; for( i = 0; i < Count; i++ ) { SimbadData.Sector[i].BlockAddress = SectorArray[i]; SimbadData.Sector[i].AccessType = AccessType; SimbadData.Sector[i].Status = Status; } // for for( i = Count; i < MAXIMUM_SIMBAD_SECTORS; i++ ) { SimbadData.Sector[i].BlockAddress = 0; SimbadData.Sector[i].AccessType = 0; SimbadData.Sector[i].Status = 0; } // for return (BOOLEAN) NT_SUCCESS( NtDeviceIoControlFile(_handle, 0, NULL, NULL, &StatusBlock, IOCTL_DISK_SIMBAD, &SimbadData, sizeof(SIMBAD_DATA), NULL, 0) ); } // SimulateBadSectors() BOOLEAN QuerySimulatedBadSectors( IN HANDLE _handle, OUT PULONG Count, IN ULONG MaximumLbnsInBuffer, OUT PLBN SectorArray, OUT PULONG AccessTypeArray ) /*++ Routine Description: This method retrieves the sectors in the bad sector simulation list. Arguments: Count -- receives the number of returned sectors MaximumLbnsInBuffer -- supplies the length of the buffer SectorArray -- receives the bad sector list AccessTypeArray -- receives the list of access types that trigger the error. Return Value: TRUE upon successful completion --*/ { SIMBAD_DATA SimbadData; IO_STATUS_BLOCK StatusBlock; ULONG ReturnedCount; ULONG i; SimbadData.Function = SIMBAD_LIST_BAD_SECTORS; if( !NT_SUCCESS( NtDeviceIoControlFile(_handle, 0, NULL, NULL, &StatusBlock, IOCTL_DISK_SIMBAD, &SimbadData, sizeof(SIMBAD_DATA), &SimbadData, sizeof(SIMBAD_DATA)) ) ) { return FALSE; } // if ReturnedCount = (SimbadData.Count < MaximumLbnsInBuffer) ? SimbadData.Count : MaximumLbnsInBuffer; for( i = 0; i < ReturnedCount; i++ ) { SectorArray[i] = SimbadData.Sector[i].BlockAddress; AccessTypeArray[i] = SimbadData.Sector[i].AccessType; } // for *Count = ReturnedCount; return TRUE; } // QuerySimulatedBadSectors() // // HEX_DIGITS is a string used to search for hex values in the input string // in ReadFromFile(). // #define HEX_DIGITS "0123456789abcdefABCDEF" VOID ReadFromFile ( ) /*++ Routine Description: This routine reads the list(s) of bad sectors for 1 or more devices and adds the sectors from that file. Arguments: None; filename is set in ParseCmdArgs as a global. Return value: None. --*/ { FILE * thefile; HANDLE _handle=0; ULONG count; ULONG access; char Buffer[MAX_DEV_NAME*8], *curptr; // curptr is current location in buffer // // if able to open the filename (specified on command line), parse it // if(thefile=fopen(filename,"r")) { // // read until file is done // while (!feof(thefile)) { // // if able to read a string, parse it. // if(fgets(Buffer, MAX_DEV_NAME*8, thefile)) { curptr=Buffer; // // Always start with the global access type value in case // user specified an option, e.g. -x. // access = accessType; // // switch based on the beginning of the buffer // switch (Buffer[0]) { // // if \ is first char, Buffer is a device name // case '\\': // // if there is an open handle, close it. // if(_handle) { NtClose(_handle); _handle=0; } // if(_handle) // // trim Buffer and use as devname for OpenDrive() // sscanf(Buffer, "%s", devname); OpenDrive(devname, &_handle); break; // // r means Buffer is a list of read-failing sectors // case 'r': case 'R': access |= SIMBAD_ACCESS_READ; goto commonRead; // // w means Buffer is a list of write-failing sectors // case 'w': case 'W': access |= SIMBAD_ACCESS_WRITE; goto commonRead; // // v means Buffer is a list of verify-failing sectors // case 'v': case 'V': access |= SIMBAD_ACCESS_VERIFY; goto commonRead; // // a means Buffer is a list of verify-failing sectors // case 'a': case 'A': access |= SIMBAD_ACCESS_VERIFY | SIMBAD_ACCESS_READ | SIMBAD_ACCESS_WRITE; commonRead: // // if there is an open drive // if (_handle) { count=0; curptr+=2; // Move past token: // // loop until no more sectors > MAX // while (0 < sscanf(curptr,"%lx", &(sectors[count])) && count