/*++ Copyright (c) 1996 Microsoft Corporation Module Name: sectorio.c Abstract: Routines to perform low-level sector I/O on either Windows NT or Windows 95. Author: Ted Miller (tedm) 1 Nov 1996 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include // // Define structures for use with Win9x VWIN32. // Note: alignment must be on 1-byte boundaries for these structures. // #include typedef struct _DIOC_REGISTERS { DWORD reg_EBX; DWORD reg_EDX; DWORD reg_ECX; DWORD reg_EAX; DWORD reg_EDI; DWORD reg_ESI; DWORD reg_Flags; } DIOC_REGISTERS; typedef struct _DIOC_DISKIO { DWORD StartSector; WORD SectorCount; LPBYTE Buffer; } DIOC_DISKIO; #include // // Local prot type // BOOL NEC98_SpecialReadOrWriteNT( IN TCHAR Drive, IN OUT LPBYTE Buffer, IN BOOL Write ); // // Define codes we care about for use with VWIN32 // #define VWIN32_DIOC_DOS_IOCTL 1 #define VWIN32_DIOC_DOS_INT25 2 #define VWIN32_DIOC_DOS_INT26 3 #define VWIN32_DIOC_DOS_DRIVEINFO 6 // new in OSR2 #if defined(_X86_) BOOL pGetWin9xLockFlagState ( IN HANDLE VWin32Vxd, IN TCHAR Drive, OUT PINT LockStatus ) { DIOC_REGISTERS RegistersIn,RegistersOut; BOOL b; DWORD SizeOut; *LockStatus = 0; // // ax = generic ioctl code // RegistersIn.reg_EAX = 0x440D; // // bx = 1-based drive number // RegistersIn.reg_EBX = (DWORD)(_totupper(Drive) - TEXT('A')) + 1; // // cx = 0x86C (get lock flag state) // RegistersIn.reg_ECX = 0x86C; // // Perform the lock and check carry. // b = DeviceIoControl( VWin32Vxd, VWIN32_DIOC_DOS_IOCTL, &RegistersIn, sizeof(DIOC_REGISTERS), &RegistersOut, sizeof(DIOC_REGISTERS), &SizeOut, NULL ); if (b) { if (RegistersOut.reg_Flags & 1) { b = FALSE; } else { *LockStatus = RegistersOut.reg_EAX; } } return b; } #endif typedef HANDLE(WINAPI *OPENTHREAD)(DWORD, BOOL, DWORD); BOOL pMakeThreadExclusive ( BOOL Lock ) { HANDLE h; THREADENTRY32 e; DWORD thisThread; HANDLE threadHandle; OPENTHREAD openThreadFn; HMODULE lib; BOOL result = FALSE; lib = LoadLibrary (TEXT("kernel32.dll")); if (!lib) { goto c0; } openThreadFn = (OPENTHREAD) GetProcAddress (lib, "OpenThread"); if (!openThreadFn) { // // Must be Win98 or Win98SE -- change thread priority as workaround // if (Lock) { result = SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); Sleep (0); } else { result = SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_NORMAL); } goto c1; } thisThread = GetCurrentThreadId(); h = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0); if (h == INVALID_HANDLE_VALUE) { goto c1; } e.dwSize = sizeof (e); if (Thread32First (h, &e)) { do { if (e.th32ThreadID != thisThread) { threadHandle = openThreadFn (THREAD_SUSPEND_RESUME, FALSE, e.th32ThreadID); if (threadHandle) { if (Lock) { SuspendThread (threadHandle); } else { ResumeThread (threadHandle); } CloseHandle (threadHandle); } } } while (Thread32Next (h, &e)); } CloseHandle (h); result = TRUE; c1: FreeLibrary (lib); c0: return result; } #if defined(_X86_) BOOL ReadOrWriteSectorsWin9xOriginal( IN HANDLE VWin32Vxd, IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN OUT LPBYTE Buffer, IN BOOL Write ) /*++ Routine Description: Common routine to read or write sectors on a disk under Windows 95 earlier than OSR2. Uses int25/26. This routine will fail on Windows NT. Arguments: VWin32Vxd - supplies Win32 handle to VWIN32 VxD. Drive - supplies drive letter of device to be read from or written to. StartSector - supplies logical sector number of first sector to be read/written. SectorCount - supplies number of sectors to be read/written. SectorSize - supplies the number of bytes in a sector on the drive to be read from/written to. Buffer - Supplies or receives data, depending on the value or the Write parameter. Write - if 0, then this is a read operastion. If non-0, then this is a write operation. Return Value: Boolean value indicating whether the disk was read/written successfully. --*/ { DIOC_REGISTERS RegistersIn,RegistersOut; DIOC_DISKIO Params; BOOL b; DWORD SizeOut; // // Set up registers and parameter block. // RegistersIn.reg_EAX = (DWORD)(TOUPPER(Drive) - TEXT('A')); RegistersIn.reg_EBX = (DWORD)&Params; RegistersIn.reg_ECX = 0xFFFF; Params.StartSector = StartSector; Params.SectorCount = (WORD)SectorCount; Params.Buffer = Buffer; // // Do the real work. // b = DeviceIoControl( VWin32Vxd, Write ? VWIN32_DIOC_DOS_INT26 : VWIN32_DIOC_DOS_INT25, &RegistersIn, sizeof(DIOC_REGISTERS), &RegistersOut, sizeof(DIOC_REGISTERS), &SizeOut, NULL ); // // Check carry flag for failure. // if(b && (RegistersOut.reg_Flags & 1)) { b = FALSE; } return(b); } BOOL ReadOrWriteSectorsWin9xOsr2( IN HANDLE VWin32Vxd, IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN OUT LPBYTE Buffer, IN BOOL Write ) /*++ Routine Description: Common routine to read or write sectors on a disk under Windows 95 OSR2 or later. Uses the new int21 function 7305 (Ext_ABSDiskReadWrite). This routine will fail on Windows NT and earlier versions of Windows 95. Arguments: VWin32Vxd - supplies Win32 handle to VWIN32 VxD. Drive - supplies drive letter of device to be read from or written to. StartSector - supplies logical sector number of first sector to be read/written. SectorCount - supplies number of sectors to be read/written. SectorSize - supplies the number of bytes in a sector on the drive to be read from/written to. Buffer - Supplies or receives data, depending on the value or the Write parameter. Write - if 0, then this is a read operastion. If non-0, then this is a write operation. Return Value: Boolean value indicating whether the disk was read/written successfully. --*/ { DIOC_REGISTERS RegistersIn,RegistersOut; DIOC_DISKIO Params; BOOL b; DWORD SizeOut; // // Set up registers and parameter block. // RegistersIn.reg_EAX = 0x7305; RegistersIn.reg_EBX = (DWORD)&Params; RegistersIn.reg_ECX = 0xFFFF; RegistersIn.reg_EDX = (DWORD)(TOUPPER(Drive) - TEXT('A')) + 1; RegistersIn.reg_ESI = Write ? 1 : 0; Params.StartSector = StartSector; Params.SectorCount = (WORD)SectorCount; Params.Buffer = Buffer; // // Do the real work. // b = DeviceIoControl( VWin32Vxd, VWIN32_DIOC_DOS_DRIVEINFO, &RegistersIn, sizeof(DIOC_REGISTERS), &RegistersOut, sizeof(DIOC_REGISTERS), &SizeOut, NULL ); // // Check carry flag for failure. // if(b && (RegistersOut.reg_Flags & 1)) { b = FALSE; } return(b); } BOOL LockOrUnlockVolumeWin9x( IN HANDLE VWin32Vxd, IN TCHAR Drive, IN UINT Level, IN BOOL Lock ) { DIOC_REGISTERS RegistersIn,RegistersOut; BOOL b; DWORD SizeOut; BOOL Pass; Pass = 0; retry: // // ax = generic ioctl code // RegistersIn.reg_EAX = 0x440d; // // bl = 1-based drive number // bh = lock level // RegistersIn.reg_EBX = (DWORD)(TOUPPER(Drive) - TEXT('A')) + 1; RegistersIn.reg_EBX |= (Level << 8); // // cl = lock or unlock volume code // ch = categoey, 8 on original Win95, 0x48 on OSR2 // RegistersIn.reg_ECX = Lock ? 0x4a : 0x6a; RegistersIn.reg_ECX |= ((ISOSR2() && !Pass) ? 0x4800 : 0x800); // // dx = permissions // // bit 0 controls write operations (0 = disallowed) // bit 1 controls read operations (0 = allowed) // RegistersIn.reg_EDX = 1; // // Perform the lock and check carry. // b = DeviceIoControl( VWin32Vxd, VWIN32_DIOC_DOS_IOCTL, &RegistersIn, sizeof(DIOC_REGISTERS), &RegistersOut, sizeof(DIOC_REGISTERS), &SizeOut, NULL ); if(b && (RegistersOut.reg_Flags & 1)) { b = FALSE; } // // If OSR2, try form of call with 8 in ch instead of 48. // if(!b && ISOSR2() && !Pass) { Pass = 1; goto retry; } return(b); } BOOL ReadOrWriteSectorsWin9x( IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN OUT LPBYTE Buffer, IN BOOL Write ) /*++ Routine Description: Common routine to read or write sectors on a disk under Windows 95. This routine will fail on Windows NT. After opening the VWIN32 VxD, the routine determines whether to use the original algorithm or the OSR2 algorithm, and calls the appropriate worker routine. Arguments: Drive - supplies drive letter of device to be read from or written to. StartSector - supplies logical sector number of first sector to be read/written. SectorCount - supplies number of sectors to be read/written. SectorSize - supplies the number of bytes in a sector on the drive to be read from/written to. Buffer - Supplies or receives data, depending on the value or the Write parameter. Write - if 0, then this is a read operastion. If non-0, then this is a write operation. Return Value: Boolean value indicating whether the disk was read/written successfully. If failure, last error is set to something meaningful. --*/ { HANDLE hVxd; BOOL b; DWORD d; INT level; INT retry = 100; // // This thread must be the exclusive thread in our process // pMakeThreadExclusive (TRUE); // // Open VWIN32.VXD // hVxd = CreateFileA( "\\\\.\\VWIN32", Write ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if(hVxd == INVALID_HANDLE_VALUE) { d = GetLastError(); b = FALSE; goto c0; } // // Take out locks. We'll be as unrestrictive as possible. // The locking stuff is really funky. You have to pass in all kinds of // different parameters in OSR2 for reasons unknown. Also the // permissions bits are strangely encoded. // if(!LockOrUnlockVolumeWin9x(hVxd,Drive,1,TRUE)) { d = ERROR_SHARING_VIOLATION; b = FALSE; goto c1; } if(!LockOrUnlockVolumeWin9x(hVxd,Drive,2,TRUE)) { d = ERROR_SHARING_VIOLATION; b = FALSE; goto c2; } // // Try to get the level 3 lock. Retry if something happened while // getting the lock. Fail after too many retries. // do { if(!LockOrUnlockVolumeWin9x(hVxd,Drive,3,TRUE)) { d = ERROR_SHARING_VIOLATION; b = FALSE; goto c3; } if (!pGetWin9xLockFlagState (hVxd, Drive, &level)) { // unexpected -- INT 21h call failed break; } if (!level) { // We successfully got a clean level 3 lock break; } LockOrUnlockVolumeWin9x(hVxd,Drive,3,FALSE); retry--; } while (retry); if (!retry) { d = ERROR_SHARING_VIOLATION; b = FALSE; goto c3; } // // Go do it. // b = ISOSR2() ? ReadOrWriteSectorsWin9xOsr2(hVxd,Drive,StartSector,SectorCount,Buffer,Write) : ReadOrWriteSectorsWin9xOriginal(hVxd,Drive,StartSector,SectorCount,Buffer,Write); // // If it failed, and OSR2 routine is being used, fall back to Win95 API. This is a workaround // for Compaq because they ship OSR2 without the new OSR2 sector API support! // if (!b && ISOSR2()) { b = ReadOrWriteSectorsWin9xOriginal(hVxd,Drive,StartSector,SectorCount,Buffer,Write); } d = GetLastError(); LockOrUnlockVolumeWin9x(hVxd,Drive,3,FALSE); c3: LockOrUnlockVolumeWin9x(hVxd,Drive,2,FALSE); c2: LockOrUnlockVolumeWin9x(hVxd,Drive,1,FALSE); c1: CloseHandle(hVxd); c0: // // Resume all threads // pMakeThreadExclusive (FALSE); SetLastError(d); return(b); } #endif BOOL ReadOrWriteSectorsWinNt( IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN UINT SectorSize, IN OUT LPBYTE Buffer, IN BOOL Write ) /*++ Routine Description: Common routine to read or write sectors on a disk under Windows NT. This routine will fail on Win9x. Arguments: Drive - supplies drive letter of device to be read from or written to. StartSector - supplies logical sector number of first sector to be read/written. SectorCount - supplies number of sectors to be read/written. SectorSize - supplies the number of bytes in a sector on the drive to be read from/written to. Buffer - Supplies or receives data, depending on the value or the Write parameter. This buffer must be aligned on a sector boundary. Write - if 0, then this is a read operastion. If non-0, then this is a write operation. Return Value: Boolean value indicating whether the disk was read/written successfully. If failure, last error is set. --*/ { HANDLE h; BOOL b; DWORD BytesXferred; TCHAR DeviceName[7]; LONGLONG Offset; LONG OffsetHigh; DWORD d; #if defined(_X86_) if (IsNEC98() && (StartSector == 0) && (SectorCount == 1)){ return(NEC98_SpecialReadOrWriteNT(Drive, Buffer, Write)); } #endif // // Open the device // wsprintf(DeviceName,TEXT("\\\\.\\%c:"),Drive); h = CreateFile( DeviceName, Write ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if(h == INVALID_HANDLE_VALUE) { d = GetLastError(); b = FALSE; goto c0; } Offset = (LONGLONG)StartSector * (LONGLONG)SectorSize; OffsetHigh = (LONG)(Offset >> 32); // // We're passing in a 64-bit offset so we have to check last error // to distinguish the error case. // if((SetFilePointer(h,(DWORD)Offset,&OffsetHigh,FILE_BEGIN) == 0xffffffff) && (GetLastError() != NO_ERROR)) { d = GetLastError(); b = FALSE; goto c1; } b = Write ? WriteFile(h,Buffer,SectorCount*SectorSize,&BytesXferred,NULL) : ReadFile(h,Buffer,SectorCount*SectorSize,&BytesXferred,NULL); d = GetLastError(); c1: CloseHandle(h); c0: SetLastError(d); return(b); } BOOL ReadOrWriteSectors( IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN UINT SectorSize, IN OUT LPBYTE Buffer, IN BOOL Write ) /*++ Routine Description: Common routine to read or write sectors on a disk. Allocates a properly aligned buffer and decides whether to call NT- or Win9x-specific i/o routine. Arguments: Drive - supplies drive letter of device to be read from or written to. StartSector - supplies logical sector number of first sector to be read/written. SectorCount - supplies number of sectors to be read/written. SectorSize - supplies the number of bytes in a sector on the drive to be read from/written to. Buffer - Supplies or receives data, depending on the value or the Write parameter. There are no alignment requirements on ths buffer. Write - if 0, then this is a read operastion. If non-0, then this is a write operation. Return Value: Boolean value indicating whether the disk was read/written successfully. Last error is undisturbed from the operation that caused any failure. --*/ { LPBYTE AlignedBuffer; LPBYTE p; BOOL b; DWORD_PTR d; // // Allocate a buffer we will align on a sector boundary. // if(AlignedBuffer = MALLOC((SectorCount * SectorSize) + (SectorSize - 1))) { if(d = (DWORD_PTR)AlignedBuffer % SectorSize) { p = (PUCHAR)((DWORD_PTR)AlignedBuffer + (SectorSize - d)); } else { p = AlignedBuffer; } if(Write) { CopyMemory(p,Buffer,SectorCount*SectorSize); } #if defined(_X86_) b = ISNT() ? ReadOrWriteSectorsWinNt(Drive,StartSector,SectorCount,SectorSize,p,Write) : ReadOrWriteSectorsWin9x(Drive,StartSector,SectorCount,p,Write); #elif defined(_AMD64_) b = ReadOrWriteSectorsWinNt(Drive,StartSector,SectorCount,SectorSize,p,Write); #else #error "Invalid Architeture" #endif d = GetLastError(); if(b && !Write) { CopyMemory(Buffer,p,SectorCount*SectorSize); } FREE(AlignedBuffer); } else { b = FALSE; d = ERROR_NOT_ENOUGH_MEMORY; } SetLastError((DWORD)d); return(b); } BOOL ReadDiskSectors( IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN UINT SectorSize, OUT LPBYTE Buffer ) /*++ Routine Description: Read a set of disk sectors off a disk device. Arguments: Drive - supplies drive letter of device to be read from. StartSector - supplies logical sector number of first sector to be read. SectorCount - supplies number of sectors to be read. SectorSize - supplies the number of bytes in a sector on the drive to be read from. Buffer - if successful, receives data from the disk. There are no alignment requirements on ths buffer. Return Value: Boolean value indicating whether the disk was read successfully. --*/ { return(ReadOrWriteSectors(Drive,StartSector,SectorCount,SectorSize,Buffer,FALSE)); } BOOL WriteDiskSectors( IN TCHAR Drive, IN UINT StartSector, IN UINT SectorCount, IN UINT SectorSize, IN LPBYTE Buffer ) /*++ Routine Description: Write data to a set of disk sectors. Arguments: Drive - supplies drive letter of device to be written to. StartSector - supplies logical sector number of first sector to be written. SectorCount - supplies number of sectors to be written. SectorSize - supplies the number of bytes in a sector on the drive to be written to. Buffer - supplies data to be written. There are no alignment requirements on ths buffer. Return Value: Boolean value indicating whether the disk was successfully written. --*/ { return(ReadOrWriteSectors(Drive,StartSector,SectorCount,SectorSize,Buffer,TRUE)); } MEDIA_TYPE GetMediaTypeNt( IN TCHAR Drive, IN PWINNT32_DRIVE_INFORMATION DriveInfo ) /*++ Routine Description: Determine the type/form-factor of a given (floppy) drive. THIS ROUTINE WORKS ONLY ON WINDOWS NT. Arguments: Drive - supplies drive letter of the drive in question. Return Value: Value from the MEDIA_TYPE enum indicating the drive type, which is derived from the largest media that the device driver indicates the drive can support. LastError is not set or preserved. --*/ { TCHAR DeviceName[7]; HANDLE h; BOOL b; BYTE Buffer[5000]; DWORD Size; DWORD d; UINT u; PDISK_GEOMETRY Geometry; MEDIA_TYPE MediaTypeOrder[] = { FixedMedia, // Fixed hard disk media RemovableMedia, // Removable media other than floppy F3_120M_512, // 3.5", 120M Floppy F3_20Pt8_512, // 3.5", 20.8MB, 512 bytes/sector F3_2Pt88_512, // 3.5", 2.88MB, 512 bytes/sector F3_1Pt44_512, // 3.5", 1.44MB, 512 bytes/sector F5_1Pt2_512, // 5.25", 1.2MB, 512 bytes/sector F3_720_512, // 3.5", 720KB, 512 bytes/sector F5_360_512, // 5.25", 360KB, 512 bytes/sector F5_320_1024, // 5.25", 320KB, 1024 bytes/sector F5_320_512, // 5.25", 320KB, 512 bytes/sector F5_180_512, // 5.25", 180KB, 512 bytes/sector F5_160_512, // 5.25", 160KB, 512 bytes/sector Unknown, // Format is unknown -1 }; // // We don't return drive information for NT // if (DriveInfo) { memset(DriveInfo, 0, sizeof(WINNT32_DRIVE_INFORMATION)); } wsprintf(DeviceName,TEXT("\\\\.\\%c:"),Drive); h = CreateFile( DeviceName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(h == INVALID_HANDLE_VALUE) { return(Unknown); } b = DeviceIoControl( h, IOCTL_DISK_GET_MEDIA_TYPES, NULL, 0, Buffer, sizeof(Buffer), &Size, NULL ); CloseHandle(h); if(!b) { return(Unknown); } Geometry = (PDISK_GEOMETRY)Buffer; // // Inefficient, but it works. // for(u=0; MediaTypeOrder[u] != -1; u++) { for(d=0; d struct { BYTE SpecialFunctions; BYTE DeviceType; WORD DeviceAttributes; WORD CylinderCount; BYTE MediaType; WORD BytesPerSector; BYTE SectorsPerCluster; WORD ReservedSectors; BYTE FatCount; WORD RootDirEntries; WORD SectorCount; BYTE MediaDescriptor; WORD SectorsPerFat; WORD SectorsPerTrack; WORD Heads; DWORD HiddenSectors; DWORD LargeSectorCount; } DeviceParams; #include // // Open VWIN32.VXD // hVxd = CreateFileA( "\\\\.\\VWIN32", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if(hVxd == INVALID_HANDLE_VALUE) { return(Unknown); } memset(&DeviceParams, 0, sizeof(DeviceParams)); // // Set up registers for IOCTL call. // RegistersIn.reg_EAX = 0x440d; // IOCTL RegistersIn.reg_EBX = (Drive - TEXT('A')) + 1; // 1-based drive in bl RegistersIn.reg_ECX = 0x860; // category = 8, func = get device params RegistersIn.reg_EDX = (DWORD)&DeviceParams; DeviceParams.SpecialFunctions = 0; b = DeviceIoControl( hVxd, VWIN32_DIOC_DOS_IOCTL, &RegistersIn, sizeof(DIOC_REGISTERS), &RegistersOut, sizeof(DIOC_REGISTERS), &SizeOut, NULL ); CloseHandle(hVxd); if(!b && !(RegistersOut.reg_Flags & 1)) { return(Unknown); } if (DriveInfo) { memset(DriveInfo, 0, sizeof(WINNT32_DRIVE_INFORMATION)); DriveInfo->CylinderCount = (DWORD)(DeviceParams.CylinderCount); DriveInfo->HeadCount = (DWORD)(DeviceParams.Heads); DriveInfo->SectorsPerTrack = (DWORD)(DeviceParams.SectorsPerTrack); DriveInfo->BytesPerSector = DeviceParams.BytesPerSector; DriveInfo->SectorCount = (ULONGLONG)((DriveInfo->CylinderCount * DriveInfo->HeadCount * DriveInfo->SectorsPerTrack)); } switch(DeviceParams.DeviceType) { case 0: type = F5_360_512; // close enough break; case 1: type = F5_1Pt2_512; break; case 2: type = F3_720_512; break; case 5: type = FixedMedia; break; case 7: type = F3_1Pt44_512; break; case 8: type = RemovableMedia; break; case 9: type = F3_2Pt88_512; break; default: type = Unknown; break; } return(type); } #endif MEDIA_TYPE GetMediaType( IN TCHAR Drive, IN PWINNT32_DRIVE_INFORMATION DriveInfo ) { #if defined(_X86_) return(ISNT() ? GetMediaTypeNt(Drive, DriveInfo) : GetMediaTypeWin9x(Drive, DriveInfo)); #elif defined(_AMD64_) return GetMediaTypeNt(Drive, DriveInfo); #else #error "Invalid Architecture" #endif } #if defined(_X86_) BOOL NEC98_SpecialReadOrWriteNT( IN TCHAR Drive, IN OUT LPBYTE Buffer, IN BOOL Write ) /*++ Routine Description: NEC98 specialn routine to boot sctor read or write sectors on a disk under Windows NT. This routine will fail on Win9x. Arguments: Drive - supplies drive letter of device to be read from or written to. Buffer - Supplies or receives data, depending on the value or the Write parameter. This buffer must be aligned on a sector boundary. Write - if 0, then this is a read operastion. If non-0, then this is a write operation. Return Value: Boolean value indicating whether the disk was read/written successfully. If failure, last error is set. --*/ { TCHAR DrivePath[4]; DWORD DontCare; DWORD SectorSize; HANDLE h; BOOL b; DWORD BytesXferred; TCHAR DeviceName[7]; LONG OffsetHigh = 0; DWORD d; LPBYTE AlignedBuffer; LPBYTE p; // // Form root path // DrivePath[0] = Drive; DrivePath[1] = TEXT(':'); DrivePath[2] = TEXT('\\'); DrivePath[3] = 0; GetDiskFreeSpace(DrivePath,&DontCare,&SectorSize,&DontCare,&DontCare); if(AlignedBuffer = MALLOC(SectorSize + SectorSize - 1)) { if(d = (DWORD)AlignedBuffer % SectorSize) { p = (PUCHAR)((DWORD)AlignedBuffer + (SectorSize - d)); } else { p = AlignedBuffer; } } else { b = FALSE; d = ERROR_NOT_ENOUGH_MEMORY; goto c0; } // // Open the device // wsprintf(DeviceName,TEXT("\\\\.\\%c:"),Drive); h = CreateFile( DeviceName, Write ? (GENERIC_WRITE | GENERIC_READ) : GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if(h == INVALID_HANDLE_VALUE) { d = GetLastError(); b = FALSE; goto c1; } if (b = ReadFile(h, p, SectorSize, &BytesXferred, NULL)){ if (Write){ CopyMemory(p, Buffer, 512); SetFilePointer(h,(DWORD)0, &OffsetHigh,FILE_BEGIN); b = WriteFile(h, p, SectorSize, &BytesXferred,NULL); } else { // read CopyMemory(Buffer, p, 512); } } d = GetLastError(); CloseHandle(h); c1: FREE(AlignedBuffer); c0: SetLastError(d); return(b); } // // Maximum partition size addressable by regular INT13 // Max capacity = sector size * cylinders (10 bits) * heads (8 bits) * // sectors / track (6 bits) // #define MAX_INT13_PARTITION_CAPACITY 8455716864L // (512 * 1024 * 256 * 63) = 7.8GB #define FAT32_BPB_HEADS_FIELD_OFFSET 0x1A #define FAT32_BPB_SECTORSPERTRACK_FIELD_OFFSET 0x18 typedef BOOL (*PGET_DISK_FREE_SPACE_EX) ( LPCTSTR, PULONGLONG, PULONGLONG, PULONGLONG ); BOOL GetDriveSize9x( TCHAR DriveLetter, PULONGLONG DriveSize ) /*++ Routine Description: Gets the total size of the given drive. NOTE : First tries GetDriveFreeSpaceEx(...) if available and if unsuccessful, tries to compute drive size using INT21h. Arguments: DriveLetter : Drive letter. DriveSize : Recieves the drive size in bytes. Return Value: TRUE, if drive size was determined otherwise FALSE. --*/ { BOOL Result = FALSE; if (DriveSize && !ISNT()) { ULONGLONG TotalDriveSize = 0; if (ISOSR2()) { HMODULE Kernel32Handle = GetModuleHandle(TEXT("kernel32.dll")); if (Kernel32Handle) { PGET_DISK_FREE_SPACE_EX GetDiskFreeSpaceExPtr; GetDiskFreeSpaceExPtr = (PGET_DISK_FREE_SPACE_EX) GetProcAddress(Kernel32Handle, "GetDiskFreeSpaceEx"); if (GetDiskFreeSpaceExPtr) { TCHAR DriveName[MAX_PATH]; ULONGLONG FreeBytes = 0, TotalSize = 0, ActualFreeBytes = 0; DriveName[0] = DriveLetter; DriveName[1] = TEXT(':'); DriveName[2] = TEXT('\\'); DriveName[3] = 0; Result = GetDiskFreeSpaceExPtr(DriveName, &FreeBytes, &TotalSize, &ActualFreeBytes); if (Result) { TotalDriveSize = TotalSize; } } } } if (!Result) { WINNT32_DRIVE_INFORMATION DriveInfo = {0}; GetMediaType(DriveLetter, &DriveInfo); if (DriveInfo.SectorCount) { TotalDriveSize = DriveInfo.SectorCount * DriveInfo.BytesPerSector; Result = TRUE; } } if (Result) { *DriveSize = TotalDriveSize; } } return Result; } #endif BOOL PatchBootCode( IN WINNT32_SYSPART_FILESYSTEM FileSystem, IN TCHAR DriveLetter, IN OUT PUCHAR BootCode, IN DWORD BootCodeSize ) /*++ Routine Description: Patches the boot code if there is any inconsistent data with the correct data NOTE : Currently we update only FAT32 BPB for invalid head count. Arguments: FileSystem : File system type to look into the buffer DriveLetter : Drive letter. BootCode : The actual boot code BootCodeSize: Size of the boot code in bytes Return Value: TRUE, if boot code was patched successfully, otherwise FALSE. --*/ { BOOL Result = FALSE; #if defined(_X86_) if (BootCode && (FileSystem != Winnt32FsUnknown)) { switch (FileSystem) { case Winnt32FsFat32: if (!ISNT() && (BootCodeSize >= (FAT32_BPB_HEADS_FIELD_OFFSET + sizeof(WORD)))) { ULONGLONG DriveSize = 0; PWORD NumberOfHeads = (PWORD)(BootCode + FAT32_BPB_HEADS_FIELD_OFFSET); PWORD SectorsPerTrack = (PWORD)(BootCode + FAT32_BPB_SECTORSPERTRACK_FIELD_OFFSET); TCHAR Buffer[MAX_PATH * 2]; Result = GetDriveSize9x(DriveLetter, &DriveSize); if (Result) { WINNT32_DRIVE_INFORMATION DriveInfo = {0}; // // Get the drive information // GetMediaType(DriveLetter, &DriveInfo); // // dump the drive information // if (DriveInfo.BytesPerSector) { _stprintf(Buffer, TEXT("Drive Information (INT 21H):\r\nCylinders:%d,Heads:%d,Sectors/Track:%d,Sectors:%I64d,Bytes/Sector:%d"), DriveInfo.CylinderCount, DriveInfo.HeadCount, DriveInfo.SectorsPerTrack, DriveInfo.SectorCount, DriveInfo.BytesPerSector); DebugLog (Winnt32LogInformation, Buffer, 0); // // The heads value better match what we got from GetMediaType(...) // If not, we patch it to the correct one. // if (DriveInfo.HeadCount && (((DWORD)(*NumberOfHeads)) != DriveInfo.HeadCount)) { _stprintf(Buffer, TEXT("Drive Information (BPB): Size=%I64u, Heads=%u, Sectors/Track=%u"), DriveSize, (*NumberOfHeads), (*SectorsPerTrack)); DebugLog(Winnt32LogInformation, TEXT("PatchBootCode: Existing %1"), 0, Buffer); // // update the heads value // *NumberOfHeads = (WORD)(DriveInfo.HeadCount); _stprintf(Buffer, TEXT("Drive Information (BPB): Size=%I64u, Heads=%u, Sectors/Track=%u"), DriveSize, (*NumberOfHeads), (*SectorsPerTrack)); DebugLog(Winnt32LogInformation, TEXT("PatchBootCode: New %1"), 0, Buffer); } } else { DebugLog(Winnt32LogError, TEXT("PatchBootCode:Failed to get the drive information"), 0); } } else { DebugLog(Winnt32LogError, TEXT("PatchBootCode:Failed to get the drive size"), 0); } } break; default : break; } } #endif return Result; }