2 // C++ Interface: diskio (Windows-specific components)
4 // Description: Class to handle low-level disk I/O for GPT fdisk
7 // Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
9 // Copyright: See COPYING file that comes with this distribution
12 // This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed
13 // under the terms of the GNU GPL version 2, as detailed in the COPYING file.
15 #define __STDC_LIMIT_MACROS
16 #define __STDC_CONSTANT_MACROS
37 // Returns the official Windows name for a shortened version of same.
38 void DiskIO::MakeRealName(void) {
41 colonPos = userFilename.find(':', 0);
42 if ((colonPos != string::npos) && (colonPos <= 3)) {
43 realFilename = "\\\\.\\physicaldrive";
44 realFilename += userFilename.substr(0, colonPos);
46 realFilename = userFilename;
48 } // DiskIO::MakeRealName()
50 // Open the currently on-record file for reading
51 int DiskIO::OpenForRead(void) {
54 if (isOpen) { // file is already open
63 fd = CreateFile(realFilename.c_str(),GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
64 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
65 if (fd == INVALID_HANDLE_VALUE) {
67 cerr << "Problem opening " << realFilename << " for reading!\n";
79 } // DiskIO::OpenForRead(void)
81 // An extended file-open function. This includes some system-specific checks.
82 // Returns 1 if the file is open, 0 otherwise....
83 int DiskIO::OpenForWrite(void) {
84 if ((isOpen) && (openForWrite))
87 // Close the disk, in case it's already open for reading only....
90 // try to open the device; may fail....
91 fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
92 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
93 FILE_ATTRIBUTE_NORMAL, NULL);
94 // Preceding call can fail when creating backup files; if so, try
95 // again with different option...
96 if (fd == INVALID_HANDLE_VALUE) {
98 fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
99 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
100 FILE_ATTRIBUTE_NORMAL, NULL);
102 if (fd == INVALID_HANDLE_VALUE) {
106 errno = GetLastError();
112 } // DiskIO::OpenForWrite(void)
114 // Close the disk device. Note that this does NOT erase the stored filenames,
115 // so the file can be re-opened without specifying the filename.
116 void DiskIO::Close(void) {
123 // Returns block size of device pointed to by fd file descriptor. If the ioctl
124 // returns an error condition, assume it's a disk file and return a value of
125 // SECTOR_SIZE (512). If the disk can't be opened at all, return a value of 0.
126 int DiskIO::GetBlockSize(void) {
127 DWORD blockSize = 0, retBytes;
128 DISK_GEOMETRY_EX geom;
130 // If disk isn't open, try to open it....
136 if (DeviceIoControl(fd, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
137 &geom, sizeof(geom), &retBytes, NULL)) {
138 blockSize = geom.Geometry.BytesPerSector;
139 } else { // was probably an ordinary file; set default value....
140 blockSize = SECTOR_SIZE;
145 } // DiskIO::GetBlockSize()
147 // Returns the number of heads, according to the kernel, or 255 if the
148 // correct value can't be determined.
149 uint32_t DiskIO::GetNumHeads(void) {
150 return UINT32_C(255);
151 } // DiskIO::GetNumHeads();
153 // Returns the number of sectors per track, according to the kernel, or 63
154 // if the correct value can't be determined.
155 uint32_t DiskIO::GetNumSecsPerTrack(void) {
157 } // DiskIO::GetNumSecsPerTrack()
159 // Resync disk caches so the OS uses the new partition table. This code varies
160 // a lot from one OS to another.
161 // Returns 1 on success, 0 if the kernel continues to use the old partition table.
162 int DiskIO::DiskSync(void) {
164 GET_LENGTH_INFORMATION buf;
167 // If disk isn't open, try to open it....
173 if (DeviceIoControl(fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, &buf, sizeof(buf), &i, NULL) == 0) {
174 cout << "Disk synchronization failed! The computer may use the old partition table\n"
175 << "until you reboot or remove and re-insert the disk!\n";
177 cout << "Disk synchronization succeeded! The computer should now use the new\n"
178 << "partition table.\n";
182 cout << "Unable to open the disk for synchronization operation! The computer will\n"
183 << "continue to use the old partition table until you reboot or remove and\n"
184 << "re-insert the disk!\n";
187 } // DiskIO::DiskSync()
189 // Seek to the specified sector. Returns 1 on success, 0 on failure.
190 int DiskIO::Seek(uint64_t sector) {
192 LARGE_INTEGER seekTo;
194 // If disk isn't open, try to open it....
196 retval = OpenForRead();
200 seekTo.QuadPart = sector * (uint64_t) GetBlockSize();
201 retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN);
203 errno = GetLastError();
204 cerr << "Error when seeking to " << seekTo.QuadPart << "! Error is " << errno << "\n";
211 // A variant on the standard read() function. Done to work around
212 // limitations in FreeBSD concerning the matching of the sector
213 // size with the number of bytes read.
214 // Returns the number of bytes read into buffer.
215 int DiskIO::Read(void* buffer, int numBytes) {
216 int blockSize = 512, i, numBlocks;
220 // If disk isn't open, try to open it....
226 // Compute required space and allocate memory
227 blockSize = GetBlockSize();
228 if (numBytes <= blockSize) {
230 tempSpace = new char [blockSize];
232 numBlocks = numBytes / blockSize;
233 if ((numBytes % blockSize) != 0)
235 tempSpace = new char [numBlocks * blockSize];
237 if (tempSpace == NULL) {
238 cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n";
242 // Read the data into temporary space, then copy it to buffer
243 ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL);
244 for (i = 0; i < numBytes; i++) {
245 ((char*) buffer)[i] = tempSpace[i];
248 // Adjust the return value, if necessary....
249 if (((numBlocks * blockSize) != numBytes) && (retval > 0))
257 // A variant on the standard write() function.
258 // Returns the number of bytes written.
259 int DiskIO::Write(void* buffer, int numBytes) {
260 int blockSize = 512, i, numBlocks, retval = 0;
264 // If disk isn't open, try to open it....
265 if ((!isOpen) || (!openForWrite)) {
270 // Compute required space and allocate memory
271 blockSize = GetBlockSize();
272 if (numBytes <= blockSize) {
274 tempSpace = new char [blockSize];
276 numBlocks = numBytes / blockSize;
277 if ((numBytes % blockSize) != 0) numBlocks++;
278 tempSpace = new char [numBlocks * blockSize];
280 if (tempSpace == NULL) {
281 cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n";
285 // Copy the data to my own buffer, then write it
286 for (i = 0; i < numBytes; i++) {
287 tempSpace[i] = ((char*) buffer)[i];
289 for (i = numBytes; i < numBlocks * blockSize; i++) {
292 WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL);
293 retval = (int) numWritten;
295 // Adjust the return value, if necessary....
296 if (((numBlocks * blockSize) != numBytes) && (retval > 0))
304 // Returns the size of the disk in blocks.
305 uint64_t DiskIO::DiskSize(int *err) {
306 uint64_t sectors = 0; // size in sectors
307 DWORD bytes, moreBytes; // low- and high-order bytes of file size
308 GET_LENGTH_INFORMATION buf;
311 // If disk isn't open, try to open it....
317 // Note to self: I recall testing a simplified version of
318 // this code, similar to what's in the __APPLE__ block,
319 // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
320 // systems but not on 64-bit. Keep this in mind in case of
321 // 32/64-bit issues on MacOS....
322 if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) {
323 sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize();
325 } else { // doesn't seem to be a disk device; assume it's an image file....
326 bytes = GetFileSize(fd, &moreBytes);
327 sectors = ((uint64_t) bytes + ((uint64_t) moreBytes) * UINT32_MAX) / GetBlockSize();
333 } // if/else (isOpen)
336 } // DiskIO::DiskSize()