diskio-windows.cc revision e321d444dcca514cf6b53459e388ddcbaab6176c
1// 2// C++ Interface: diskio (Windows-specific components) 3// 4// Description: Class to handle low-level disk I/O for GPT fdisk 5// 6// 7// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009 8// 9// Copyright: See COPYING file that comes with this distribution 10// 11// 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. 14 15#define __STDC_LIMIT_MACROS 16#define __STDC_CONSTANT_MACROS 17 18#include <windows.h> 19#include <winioctl.h> 20#define fstat64 fstat 21#define stat64 stat 22#define S_IRGRP 0 23#define S_IROTH 0 24#include <stdio.h> 25#include <string> 26#include <stdint.h> 27#include <errno.h> 28#include <fcntl.h> 29#include <sys/stat.h> 30#include <iostream> 31 32#include "support.h" 33#include "diskio.h" 34 35using namespace std; 36 37// Returns the official Windows name for a shortened version of same. 38void DiskIO::MakeRealName(void) { 39 size_t colonPos; 40 41 colonPos = userFilename.find(':', 0); 42 if ((colonPos != string::npos) && (colonPos <= 3)) { 43 realFilename = "\\\\.\\physicaldrive"; 44 realFilename += userFilename.substr(0, colonPos); 45 } else { 46 realFilename = userFilename; 47 } // if/else 48} // DiskIO::MakeRealName() 49 50// Open the currently on-record file for reading 51int DiskIO::OpenForRead(void) { 52 int shouldOpen = 1; 53 54 if (isOpen) { // file is already open 55 if (openForWrite) { 56 Close(); 57 } else { 58 shouldOpen = 0; 59 } // if/else 60 } // if 61 62 if (shouldOpen) { 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) { 66 CloseHandle(fd); 67 cerr << "Problem opening " << realFilename << " for reading!\n"; 68 realFilename = ""; 69 userFilename = ""; 70 isOpen = 0; 71 openForWrite = 0; 72 } else { 73 isOpen = 1; 74 openForWrite = 0; 75 } // if/else 76 } // if 77 78 return isOpen; 79} // DiskIO::OpenForRead(void) 80 81// An extended file-open function. This includes some system-specific checks. 82// Returns 1 if the file is open, 0 otherwise.... 83int DiskIO::OpenForWrite(void) { 84 if ((isOpen) && (openForWrite)) 85 return 1; 86 87 // Close the disk, in case it's already open for reading only.... 88 Close(); 89 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 if (fd == INVALID_HANDLE_VALUE) { 95 CloseHandle(fd); 96 isOpen = 0; 97 openForWrite = 0; 98 errno = GetLastError(); 99 } else { 100 isOpen = 1; 101 openForWrite = 1; 102 } // if/else 103 return isOpen; 104} // DiskIO::OpenForWrite(void) 105 106// Close the disk device. Note that this does NOT erase the stored filenames, 107// so the file can be re-opened without specifying the filename. 108void DiskIO::Close(void) { 109 if (isOpen) 110 CloseHandle(fd); 111 isOpen = 0; 112 openForWrite = 0; 113} // DiskIO::Close() 114 115// Returns block size of device pointed to by fd file descriptor. If the ioctl 116// returns an error condition, print a warning but return a value of SECTOR_SIZE 117// (512).. 118int DiskIO::GetBlockSize(void) { 119 int err; 120 DWORD blockSize, junk1, junk2, junk3; 121 122 // If disk isn't open, try to open it.... 123 if (!isOpen) { 124 OpenForRead(); 125 } // if 126 127 if (isOpen) { 128/* BOOL WINAPI GetDiskFreeSpace( 129 __in LPCTSTR lpRootPathName, 130 __out LPDWORD lpSectorsPerCluster, 131 __out LPDWORD lpBytesPerSector, 132 __out LPDWORD lpNumberOfFreeClusters, 133 __out LPDWORD lpTotalNumberOfClusters 134 ); */ 135// err = GetDiskFreeSpace(realFilename.c_str(), &junk1, &blockSize, &junk2, &junk3); 136 // Above call is fubared -- returns weird values for blockSize.... 137 err = 1; 138 blockSize = 512; 139 140 if (err == 0) { 141 blockSize = SECTOR_SIZE; 142 // ENOTTY = inappropriate ioctl; probably being called on a disk image 143 // file, so don't display the warning message.... 144 // 32-bit code returns EINVAL, I don't know why. I know I'm treading on 145 // thin ice here, but it should be OK in all but very weird cases.... 146 if (errno != 267) { // 267 is returned on ordinary files 147 cerr << "\aError " << GetLastError() << " when determining sector size! " 148 << "Setting sector size to " << SECTOR_SIZE << "\n"; 149 } // if 150 } // if (err == -1) 151 } // if (isOpen) 152 153 return (blockSize); 154} // DiskIO::GetBlockSize() 155 156// Resync disk caches so the OS uses the new partition table. This code varies 157// a lot from one OS to another. 158void DiskIO::DiskSync(void) { 159 DWORD i; 160 GET_LENGTH_INFORMATION buf; 161 162 // If disk isn't open, try to open it.... 163 if (!openForWrite) { 164 OpenForWrite(); 165 } // if 166 167 if (isOpen) { 168 if (DeviceIoControl(fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, &buf, sizeof(buf), &i, NULL) == 0) { 169 cout << "Disk synchronization failed! The computer may use the old partition table\n" 170 << "until you reboot or remove and re-insert the disk!\n"; 171 } else { 172 cout << "Disk synchronization succeeded! The computer should now use the new\n" 173 << "partition table.\n"; 174 } // if/else 175 } else { 176 cout << "Unable to open the disk for synchronization operation! The computer will\n" 177 << "continue to use the old partition table until you reboot or remove and\n" 178 << "re-insert the disk!\n"; 179 } // if (isOpen) 180} // DiskIO::DiskSync() 181 182// Seek to the specified sector. Returns 1 on success, 0 on failure. 183int DiskIO::Seek(uint64_t sector) { 184 int retval = 1; 185 LARGE_INTEGER seekTo; 186 187 // If disk isn't open, try to open it.... 188 if (!isOpen) { 189 retval = OpenForRead(); 190 } // if 191 192 if (isOpen) { 193 seekTo.QuadPart = sector * (uint64_t) GetBlockSize(); 194 retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN); 195 if (retval == 0) { 196 errno = GetLastError(); 197 cerr << "Error when seeking to " << seekTo.QuadPart << "! Error is " << errno << "\n"; 198 retval = 0; 199 } // if 200 } // if 201 return retval; 202} // DiskIO::Seek() 203 204// A variant on the standard read() function. Done to work around 205// limitations in FreeBSD concerning the matching of the sector 206// size with the number of bytes read. 207// Returns the number of bytes read into buffer. 208int DiskIO::Read(void* buffer, int numBytes) { 209 int blockSize = 512, i, numBlocks; 210 char* tempSpace; 211 DWORD retval = 0; 212 213 // If disk isn't open, try to open it.... 214 if (!isOpen) { 215 OpenForRead(); 216 } // if 217 218 if (isOpen) { 219 // Compute required space and allocate memory 220 blockSize = GetBlockSize(); 221 if (numBytes <= blockSize) { 222 numBlocks = 1; 223 tempSpace = (char*) malloc(blockSize); 224 } else { 225 numBlocks = numBytes / blockSize; 226 if ((numBytes % blockSize) != 0) numBlocks++; 227 tempSpace = (char*) malloc(numBlocks * blockSize); 228 } // if/else 229 230 // Read the data into temporary space, then copy it to buffer 231 ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL); 232 for (i = 0; i < numBytes; i++) { 233 ((char*) buffer)[i] = tempSpace[i]; 234 } // for 235 236 // Adjust the return value, if necessary.... 237 if (((numBlocks * blockSize) != numBytes) && (retval > 0)) 238 retval = numBytes; 239 240 free(tempSpace); 241 } // if (isOpen) 242 return retval; 243} // DiskIO::Read() 244 245// A variant on the standard write() function. Done to work around 246// limitations in FreeBSD concerning the matching of the sector 247// size with the number of bytes read. 248// Returns the number of bytes written. 249int DiskIO::Write(void* buffer, int numBytes) { 250 int blockSize = 512, i, numBlocks, retval = 0; 251 char* tempSpace; 252 DWORD numWritten; 253 254 // If disk isn't open, try to open it.... 255 if ((!isOpen) || (!openForWrite)) { 256 OpenForWrite(); 257 } // if 258 259 if (isOpen) { 260 // Compute required space and allocate memory 261 blockSize = GetBlockSize(); 262 if (numBytes <= blockSize) { 263 numBlocks = 1; 264 tempSpace = (char*) malloc(blockSize); 265 } else { 266 numBlocks = numBytes / blockSize; 267 if ((numBytes % blockSize) != 0) numBlocks++; 268 tempSpace = (char*) malloc(numBlocks * blockSize); 269 } // if/else 270 271 // Copy the data to my own buffer, then write it 272 for (i = 0; i < numBytes; i++) { 273 tempSpace[i] = ((char*) buffer)[i]; 274 } // for 275 for (i = numBytes; i < numBlocks * blockSize; i++) { 276 tempSpace[i] = 0; 277 } // for 278 WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL); 279 retval = (int) numWritten; 280 281 // Adjust the return value, if necessary.... 282 if (((numBlocks * blockSize) != numBytes) && (retval > 0)) 283 retval = numBytes; 284 285 free(tempSpace); 286 } // if (isOpen) 287 return retval; 288} // DiskIO:Write() 289 290// Returns the size of the disk in blocks. 291uint64_t DiskIO::DiskSize(int *err) { 292 uint64_t sectors = 0; // size in sectors 293 DWORD bytes, moreBytes; // low- and high-order bytes of file size 294 GET_LENGTH_INFORMATION buf; 295 DWORD i; 296 297 // If disk isn't open, try to open it.... 298 if (!isOpen) { 299 OpenForRead(); 300 } // if 301 302 if (isOpen) { 303 // Note to self: I recall testing a simplified version of 304 // this code, similar to what's in the __APPLE__ block, 305 // on Linux, but I had some problems. IIRC, it ran OK on 32-bit 306 // systems but not on 64-bit. Keep this in mind in case of 307 // 32/64-bit issues on MacOS.... 308 if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) { 309 sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize(); 310 *err = 0; 311 } else { // doesn't seem to be a disk device; assume it's an image file.... 312 bytes = GetFileSize(fd, &moreBytes); 313 sectors = ((uint64_t) bytes + ((uint64_t) moreBytes) * UINT32_MAX) / GetBlockSize(); 314 *err = 0; 315 } // if 316 } else { 317 *err = -1; 318 sectors = 0; 319 } // if/else (isOpen) 320 321 return sectors; 322} // DiskIO::DiskSize() 323