1add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
2add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// C++ Interface: diskio (Windows-specific components)
3add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
4add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Description: Class to handle low-level disk I/O for GPT fdisk
5add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
6add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
7add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
8add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
9add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Copyright: See COPYING file that comes with this distribution
10add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
11add79a6e1b3a1af1305f02d51eb3aa148f580caasrs//
12add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed
13add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
14add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
15add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#define __STDC_LIMIT_MACROS
16add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#define __STDC_CONSTANT_MACROS
17add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
18add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <windows.h>
19add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <winioctl.h>
20add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#define fstat64 fstat
21add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#define stat64 stat
2208bb0da07953af605b4918e268272de15ac151aasrs#define S_IRGRP 0
23add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#define S_IROTH 0
24add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <stdio.h>
25add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <string>
26add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <stdint.h>
27add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <errno.h>
28add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <fcntl.h>
29add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <sys/stat.h>
30add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include <iostream>
31add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
32add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include "support.h"
33add79a6e1b3a1af1305f02d51eb3aa148f580caasrs#include "diskio.h"
34add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
35add79a6e1b3a1af1305f02d51eb3aa148f580caasrsusing namespace std;
36add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
37add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Returns the official Windows name for a shortened version of same.
38add79a6e1b3a1af1305f02d51eb3aa148f580caasrsvoid DiskIO::MakeRealName(void) {
39e321d444dcca514cf6b53459e388ddcbaab6176csrs   size_t colonPos;
40add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
41add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   colonPos = userFilename.find(':', 0);
42add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if ((colonPos != string::npos) && (colonPos <= 3)) {
43add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      realFilename = "\\\\.\\physicaldrive";
44add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      realFilename += userFilename.substr(0, colonPos);
450a6973119c9e9984ad47a6da3231e8d16f996c5csrs   } else {
460a6973119c9e9984ad47a6da3231e8d16f996c5csrs      realFilename = userFilename;
47add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if/else
48add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::MakeRealName()
49add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
50add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Open the currently on-record file for reading
51add79a6e1b3a1af1305f02d51eb3aa148f580caasrsint DiskIO::OpenForRead(void) {
52add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   int shouldOpen = 1;
53add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
54add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) { // file is already open
55add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (openForWrite) {
56add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         Close();
57add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } else {
58add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         shouldOpen = 0;
59add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // if/else
60add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
61add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
62add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (shouldOpen) {
63add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      fd = CreateFile(realFilename.c_str(),GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
64add79a6e1b3a1af1305f02d51eb3aa148f580caasrs                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
65add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (fd == INVALID_HANDLE_VALUE) {
66add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         CloseHandle(fd);
6755d926192adc984462509b2966e23bc0d1129bbdsrs         cerr << "Problem opening " << realFilename << " for reading!\n";
68add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         realFilename = "";
69add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         userFilename = "";
70add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         isOpen = 0;
71add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         openForWrite = 0;
72add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } else {
73add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         isOpen = 1;
74add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         openForWrite = 0;
75add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // if/else
76add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
77add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
78add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return isOpen;
79add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::OpenForRead(void)
80add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
81add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// An extended file-open function. This includes some system-specific checks.
82add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Returns 1 if the file is open, 0 otherwise....
83add79a6e1b3a1af1305f02d51eb3aa148f580caasrsint DiskIO::OpenForWrite(void) {
84add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if ((isOpen) && (openForWrite))
85add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      return 1;
86add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
87add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // Close the disk, in case it's already open for reading only....
88add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   Close();
89add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
90add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // try to open the device; may fail....
91add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
92add79a6e1b3a1af1305f02d51eb3aa148f580caasrs                   FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
93add79a6e1b3a1af1305f02d51eb3aa148f580caasrs                   FILE_ATTRIBUTE_NORMAL, NULL);
9408bb0da07953af605b4918e268272de15ac151aasrs   // Preceding call can fail when creating backup files; if so, try
9508bb0da07953af605b4918e268272de15ac151aasrs   // again with different option...
9608bb0da07953af605b4918e268272de15ac151aasrs   if (fd == INVALID_HANDLE_VALUE) {
9708bb0da07953af605b4918e268272de15ac151aasrs      CloseHandle(fd);
9808bb0da07953af605b4918e268272de15ac151aasrs      fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
9908bb0da07953af605b4918e268272de15ac151aasrs                      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
10008bb0da07953af605b4918e268272de15ac151aasrs                      FILE_ATTRIBUTE_NORMAL, NULL);
10108bb0da07953af605b4918e268272de15ac151aasrs   } // if
102add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (fd == INVALID_HANDLE_VALUE) {
103add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      CloseHandle(fd);
104add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      isOpen = 0;
105add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      openForWrite = 0;
1060a6973119c9e9984ad47a6da3231e8d16f996c5csrs      errno = GetLastError();
1070a6973119c9e9984ad47a6da3231e8d16f996c5csrs   } else {
1080a6973119c9e9984ad47a6da3231e8d16f996c5csrs      isOpen = 1;
1090a6973119c9e9984ad47a6da3231e8d16f996c5csrs      openForWrite = 1;
110add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if/else
111add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return isOpen;
112add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::OpenForWrite(void)
113add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
114add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Close the disk device. Note that this does NOT erase the stored filenames,
115add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// so the file can be re-opened without specifying the filename.
116add79a6e1b3a1af1305f02d51eb3aa148f580caasrsvoid DiskIO::Close(void) {
117add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen)
118add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      CloseHandle(fd);
119add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   isOpen = 0;
120add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   openForWrite = 0;
121add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::Close()
122add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
123add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Returns block size of device pointed to by fd file descriptor. If the ioctl
12455d926192adc984462509b2966e23bc0d1129bbdsrs// returns an error condition, assume it's a disk file and return a value of
12555d926192adc984462509b2966e23bc0d1129bbdsrs// SECTOR_SIZE (512). If the disk can't be opened at all, return a value of 0.
126add79a6e1b3a1af1305f02d51eb3aa148f580caasrsint DiskIO::GetBlockSize(void) {
12755d926192adc984462509b2966e23bc0d1129bbdsrs   DWORD blockSize = 0, retBytes;
12855d926192adc984462509b2966e23bc0d1129bbdsrs   DISK_GEOMETRY_EX geom;
129add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
130add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // If disk isn't open, try to open it....
131add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (!isOpen) {
132add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      OpenForRead();
133add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
134add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
135add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) {
136699941e25a1fcf0beec124203747c8ed20842989srs      if (DeviceIoControl(fd, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
137699941e25a1fcf0beec124203747c8ed20842989srs                          &geom, sizeof(geom), &retBytes, NULL)) {
13855d926192adc984462509b2966e23bc0d1129bbdsrs         blockSize = geom.Geometry.BytesPerSector;
139699941e25a1fcf0beec124203747c8ed20842989srs      } else { // was probably an ordinary file; set default value....
140add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         blockSize = SECTOR_SIZE;
141699941e25a1fcf0beec124203747c8ed20842989srs      } // if/else
142add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if (isOpen)
143add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
144add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return (blockSize);
145add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::GetBlockSize()
146add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
147bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs// Returns the number of heads, according to the kernel, or 255 if the
148bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs// correct value can't be determined.
149bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrsuint32_t DiskIO::GetNumHeads(void) {
150bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs   return UINT32_C(255);
151bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs} // DiskIO::GetNumHeads();
152bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs
153bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs// Returns the number of sectors per track, according to the kernel, or 63
154bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs// if the correct value can't be determined.
155bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrsuint32_t DiskIO::GetNumSecsPerTrack(void) {
156bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs   return UINT32_C(63);
157bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs} // DiskIO::GetNumSecsPerTrack()
158bf8950cad0285ee6ab8a896e8d0a30c5fb62c7afsrs
159add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Resync disk caches so the OS uses the new partition table. This code varies
160add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// a lot from one OS to another.
161a17fe69ec07c93a24894e4c4243f05af2bfc5bd7srs// Returns 1 on success, 0 if the kernel continues to use the old partition table.
162a17fe69ec07c93a24894e4c4243f05af2bfc5bd7srsint DiskIO::DiskSync(void) {
1630a6973119c9e9984ad47a6da3231e8d16f996c5csrs   DWORD i;
1640a6973119c9e9984ad47a6da3231e8d16f996c5csrs   GET_LENGTH_INFORMATION buf;
165a17fe69ec07c93a24894e4c4243f05af2bfc5bd7srs   int retval = 0;
166add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
167add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // If disk isn't open, try to open it....
1680a6973119c9e9984ad47a6da3231e8d16f996c5csrs   if (!openForWrite) {
1690a6973119c9e9984ad47a6da3231e8d16f996c5csrs      OpenForWrite();
170add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
171add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
172add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) {
1730a6973119c9e9984ad47a6da3231e8d16f996c5csrs      if (DeviceIoControl(fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, &buf, sizeof(buf), &i, NULL) == 0) {
1740a6973119c9e9984ad47a6da3231e8d16f996c5csrs         cout << "Disk synchronization failed! The computer may use the old partition table\n"
1750a6973119c9e9984ad47a6da3231e8d16f996c5csrs              << "until you reboot or remove and re-insert the disk!\n";
1760a6973119c9e9984ad47a6da3231e8d16f996c5csrs      } else {
1770a6973119c9e9984ad47a6da3231e8d16f996c5csrs         cout << "Disk synchronization succeeded! The computer should now use the new\n"
1780a6973119c9e9984ad47a6da3231e8d16f996c5csrs              << "partition table.\n";
179a17fe69ec07c93a24894e4c4243f05af2bfc5bd7srs         retval = 1;
1800a6973119c9e9984ad47a6da3231e8d16f996c5csrs      } // if/else
1810a6973119c9e9984ad47a6da3231e8d16f996c5csrs   } else {
1820a6973119c9e9984ad47a6da3231e8d16f996c5csrs      cout << "Unable to open the disk for synchronization operation! The computer will\n"
1830a6973119c9e9984ad47a6da3231e8d16f996c5csrs           << "continue to use the old partition table until you reboot or remove and\n"
1840a6973119c9e9984ad47a6da3231e8d16f996c5csrs           << "re-insert the disk!\n";
185add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if (isOpen)
186a17fe69ec07c93a24894e4c4243f05af2bfc5bd7srs   return retval;
187add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::DiskSync()
188add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
189add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Seek to the specified sector. Returns 1 on success, 0 on failure.
190add79a6e1b3a1af1305f02d51eb3aa148f580caasrsint DiskIO::Seek(uint64_t sector) {
191add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   int retval = 1;
192add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   LARGE_INTEGER seekTo;
193add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
194add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // If disk isn't open, try to open it....
195add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (!isOpen) {
196add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      retval = OpenForRead();
197add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
198add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
199add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) {
2000a6973119c9e9984ad47a6da3231e8d16f996c5csrs      seekTo.QuadPart = sector * (uint64_t) GetBlockSize();
201add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN);
202add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (retval == 0) {
203add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         errno = GetLastError();
2040a6973119c9e9984ad47a6da3231e8d16f996c5csrs         cerr << "Error when seeking to " << seekTo.QuadPart << "! Error is " << errno << "\n";
205add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         retval = 0;
206add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // if
207add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
208add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return retval;
209add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::Seek()
210add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
211add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// A variant on the standard read() function. Done to work around
212add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// limitations in FreeBSD concerning the matching of the sector
213add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// size with the number of bytes read.
214add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Returns the number of bytes read into buffer.
215add79a6e1b3a1af1305f02d51eb3aa148f580caasrsint DiskIO::Read(void* buffer, int numBytes) {
216add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   int blockSize = 512, i, numBlocks;
217add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   char* tempSpace;
218add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   DWORD retval = 0;
219add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
220add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // If disk isn't open, try to open it....
221add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (!isOpen) {
222add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      OpenForRead();
223add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
224add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
225add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) {
226add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Compute required space and allocate memory
227add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      blockSize = GetBlockSize();
228add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (numBytes <= blockSize) {
229add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         numBlocks = 1;
230cb76c673eeb84344887715d36d44b799042be5a5srs         tempSpace = new char [blockSize];
231add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } else {
232add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         numBlocks = numBytes / blockSize;
233cb76c673eeb84344887715d36d44b799042be5a5srs         if ((numBytes % blockSize) != 0)
234cb76c673eeb84344887715d36d44b799042be5a5srs            numBlocks++;
235cb76c673eeb84344887715d36d44b799042be5a5srs         tempSpace = new char [numBlocks * blockSize];
236add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // if/else
2376aae2a9b70e9f88926baad94c1eea40e0b534f01srs      if (tempSpace == NULL) {
2386aae2a9b70e9f88926baad94c1eea40e0b534f01srs         cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n";
2396aae2a9b70e9f88926baad94c1eea40e0b534f01srs         exit(1);
2406aae2a9b70e9f88926baad94c1eea40e0b534f01srs      } // if
2413488294d718a0e8b7f312c80c9e5729671173f6asrs
242add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Read the data into temporary space, then copy it to buffer
243add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL);
244add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      for (i = 0; i < numBytes; i++) {
245add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         ((char*) buffer)[i] = tempSpace[i];
246add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // for
247add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
248add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Adjust the return value, if necessary....
249add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (((numBlocks * blockSize) != numBytes) && (retval > 0))
250add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         retval = numBytes;
251add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
252cb76c673eeb84344887715d36d44b799042be5a5srs      delete[] tempSpace;
253add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if (isOpen)
254add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return retval;
255add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::Read()
256add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
2574307ef2e863cbec357df56197046c6b679fc5d2dsrs// A variant on the standard write() function.
258add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Returns the number of bytes written.
259add79a6e1b3a1af1305f02d51eb3aa148f580caasrsint DiskIO::Write(void* buffer, int numBytes) {
260add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   int blockSize = 512, i, numBlocks, retval = 0;
261add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   char* tempSpace;
262add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   DWORD numWritten;
263add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
264add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // If disk isn't open, try to open it....
265add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if ((!isOpen) || (!openForWrite)) {
266add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      OpenForWrite();
267add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
268add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
269add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) {
270add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Compute required space and allocate memory
271add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      blockSize = GetBlockSize();
272add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (numBytes <= blockSize) {
273add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         numBlocks = 1;
274cb76c673eeb84344887715d36d44b799042be5a5srs         tempSpace = new char [blockSize];
275add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } else {
276add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         numBlocks = numBytes / blockSize;
277add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         if ((numBytes % blockSize) != 0) numBlocks++;
278cb76c673eeb84344887715d36d44b799042be5a5srs         tempSpace = new char [numBlocks * blockSize];
279add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // if/else
2806aae2a9b70e9f88926baad94c1eea40e0b534f01srs      if (tempSpace == NULL) {
2816aae2a9b70e9f88926baad94c1eea40e0b534f01srs         cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n";
2826aae2a9b70e9f88926baad94c1eea40e0b534f01srs         exit(1);
2836aae2a9b70e9f88926baad94c1eea40e0b534f01srs      } // if
284add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
285add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Copy the data to my own buffer, then write it
286add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      for (i = 0; i < numBytes; i++) {
287add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         tempSpace[i] = ((char*) buffer)[i];
288add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // for
289add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      for (i = numBytes; i < numBlocks * blockSize; i++) {
290add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         tempSpace[i] = 0;
291add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      } // for
292add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL);
293add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      retval = (int) numWritten;
294add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
295add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Adjust the return value, if necessary....
296add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (((numBlocks * blockSize) != numBytes) && (retval > 0))
297add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         retval = numBytes;
298add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
299cb76c673eeb84344887715d36d44b799042be5a5srs      delete[] tempSpace;
300add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if (isOpen)
301add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return retval;
302add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO:Write()
303add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
304add79a6e1b3a1af1305f02d51eb3aa148f580caasrs// Returns the size of the disk in blocks.
305add79a6e1b3a1af1305f02d51eb3aa148f580caasrsuint64_t DiskIO::DiskSize(int *err) {
306add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   uint64_t sectors = 0; // size in sectors
3070a6973119c9e9984ad47a6da3231e8d16f996c5csrs   DWORD bytes, moreBytes; // low- and high-order bytes of file size
308add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   GET_LENGTH_INFORMATION buf;
309add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   DWORD i;
310add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
311add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   // If disk isn't open, try to open it....
312add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (!isOpen) {
313add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      OpenForRead();
314add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   } // if
315add79a6e1b3a1af1305f02d51eb3aa148f580caasrs
316add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   if (isOpen) {
317add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // Note to self: I recall testing a simplified version of
318add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // this code, similar to what's in the __APPLE__ block,
319add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
320add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // systems but not on 64-bit. Keep this in mind in case of
321add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      // 32/64-bit issues on MacOS....
322add79a6e1b3a1af1305f02d51eb3aa148f580caasrs      if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) {
323add79a6e1b3a1af1305f02d51eb3aa148f580caasrs         sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize();
3240a6973119c9e9984ad47a6da3231e8d16f996c5csrs         *err = 0;
3250a6973119c9e9984ad47a6da3231e8d16f996c5csrs      } else { // doesn't seem to be a disk device; assume it's an image file....
3260a6973119c9e9984ad47a6da3231e8d16f996c5csrs         bytes = GetFileSize(fd, &moreBytes);
3270a6973119c9e9984ad47a6da3231e8d16f996c5csrs         sectors = ((uint64_t) bytes + ((uint64_t) moreBytes) * UINT32_MAX) / GetBlockSize();
3280a6973119c9e9984ad47a6da3231e8d16f996c5csrs         *err = 0;
3290a6973119c9e9984ad47a6da3231e8d16f996c5csrs      } // if
3300a6973119c9e9984ad47a6da3231e8d16f996c5csrs   } else {
3310a6973119c9e9984ad47a6da3231e8d16f996c5csrs      *err = -1;
3320a6973119c9e9984ad47a6da3231e8d16f996c5csrs      sectors = 0;
3330a6973119c9e9984ad47a6da3231e8d16f996c5csrs   } // if/else (isOpen)
3340a6973119c9e9984ad47a6da3231e8d16f996c5csrs
335add79a6e1b3a1af1305f02d51eb3aa148f580caasrs   return sectors;
336add79a6e1b3a1af1305f02d51eb3aa148f580caasrs} // DiskIO::DiskSize()
337