blob: 80cd7f25bd8cf3f120f3079485053272c204e4b1 [file] [log] [blame]
srs5694add79a62010-01-26 15:59:58 -05001//
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
srs569408bb0da2010-02-19 17:19:55 -050022#define S_IRGRP 0
srs5694add79a62010-01-26 15:59:58 -050023#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) {
srs5694e321d442010-01-29 17:44:04 -050039 size_t colonPos;
srs5694add79a62010-01-26 15:59:58 -050040
41 colonPos = userFilename.find(':', 0);
42 if ((colonPos != string::npos) && (colonPos <= 3)) {
43 realFilename = "\\\\.\\physicaldrive";
44 realFilename += userFilename.substr(0, colonPos);
srs56940a697312010-01-28 21:10:52 -050045 } else {
46 realFilename = userFilename;
srs5694add79a62010-01-26 15:59:58 -050047 } // if/else
srs5694add79a62010-01-26 15:59:58 -050048} // 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) {
srs5694add79a62010-01-26 15:59:58 -050063 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);
srs569455d92612010-03-07 22:16:07 -050067 cerr << "Problem opening " << realFilename << " for reading!\n";
srs5694add79a62010-01-26 15:59:58 -050068 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);
srs569408bb0da2010-02-19 17:19:55 -050094 // Preceding call can fail when creating backup files; if so, try
95 // again with different option...
96 if (fd == INVALID_HANDLE_VALUE) {
97 CloseHandle(fd);
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);
101 } // if
srs5694add79a62010-01-26 15:59:58 -0500102 if (fd == INVALID_HANDLE_VALUE) {
103 CloseHandle(fd);
srs5694add79a62010-01-26 15:59:58 -0500104 isOpen = 0;
105 openForWrite = 0;
srs56940a697312010-01-28 21:10:52 -0500106 errno = GetLastError();
107 } else {
108 isOpen = 1;
109 openForWrite = 1;
srs5694add79a62010-01-26 15:59:58 -0500110 } // if/else
111 return isOpen;
112} // DiskIO::OpenForWrite(void)
113
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.
116void DiskIO::Close(void) {
117 if (isOpen)
118 CloseHandle(fd);
119 isOpen = 0;
120 openForWrite = 0;
121} // DiskIO::Close()
122
123// Returns block size of device pointed to by fd file descriptor. If the ioctl
srs569455d92612010-03-07 22:16:07 -0500124// 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.
srs5694add79a62010-01-26 15:59:58 -0500126int DiskIO::GetBlockSize(void) {
srs569455d92612010-03-07 22:16:07 -0500127 DWORD blockSize = 0, retBytes;
128 DISK_GEOMETRY_EX geom;
srs5694add79a62010-01-26 15:59:58 -0500129
130 // If disk isn't open, try to open it....
131 if (!isOpen) {
132 OpenForRead();
133 } // if
134
135 if (isOpen) {
srs5694699941e2011-03-21 21:33:57 -0400136 if (DeviceIoControl(fd, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
137 &geom, sizeof(geom), &retBytes, NULL)) {
srs569455d92612010-03-07 22:16:07 -0500138 blockSize = geom.Geometry.BytesPerSector;
srs5694699941e2011-03-21 21:33:57 -0400139 } else { // was probably an ordinary file; set default value....
srs5694add79a62010-01-26 15:59:58 -0500140 blockSize = SECTOR_SIZE;
srs5694699941e2011-03-21 21:33:57 -0400141 } // if/else
srs5694add79a62010-01-26 15:59:58 -0500142 } // if (isOpen)
143
144 return (blockSize);
145} // DiskIO::GetBlockSize()
146
Rod Smithfc0e0142017-07-25 21:33:18 -0400147// In theory, returns the physical block size. In practice, this is only
148// supported in Linux, as of yet.
149// TODO: Get this working in Windows.
Rod Smith7dfc8962017-07-26 19:45:51 -0400150int DiskIO::GetPhysBlockSize(void) {
Rod Smithfc0e0142017-07-25 21:33:18 -0400151 return 0;
152} // DiskIO::GetPhysBlockSize()
153
srs5694bf8950c2011-03-12 01:23:12 -0500154// Returns the number of heads, according to the kernel, or 255 if the
155// correct value can't be determined.
156uint32_t DiskIO::GetNumHeads(void) {
157 return UINT32_C(255);
158} // DiskIO::GetNumHeads();
159
160// Returns the number of sectors per track, according to the kernel, or 63
161// if the correct value can't be determined.
162uint32_t DiskIO::GetNumSecsPerTrack(void) {
163 return UINT32_C(63);
164} // DiskIO::GetNumSecsPerTrack()
165
srs5694add79a62010-01-26 15:59:58 -0500166// Resync disk caches so the OS uses the new partition table. This code varies
167// a lot from one OS to another.
srs5694a17fe692011-09-10 20:30:20 -0400168// Returns 1 on success, 0 if the kernel continues to use the old partition table.
169int DiskIO::DiskSync(void) {
srs56940a697312010-01-28 21:10:52 -0500170 DWORD i;
171 GET_LENGTH_INFORMATION buf;
srs5694a17fe692011-09-10 20:30:20 -0400172 int retval = 0;
srs5694add79a62010-01-26 15:59:58 -0500173
174 // If disk isn't open, try to open it....
srs56940a697312010-01-28 21:10:52 -0500175 if (!openForWrite) {
176 OpenForWrite();
srs5694add79a62010-01-26 15:59:58 -0500177 } // if
178
179 if (isOpen) {
srs56940a697312010-01-28 21:10:52 -0500180 if (DeviceIoControl(fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, &buf, sizeof(buf), &i, NULL) == 0) {
181 cout << "Disk synchronization failed! The computer may use the old partition table\n"
182 << "until you reboot or remove and re-insert the disk!\n";
183 } else {
184 cout << "Disk synchronization succeeded! The computer should now use the new\n"
185 << "partition table.\n";
srs5694a17fe692011-09-10 20:30:20 -0400186 retval = 1;
srs56940a697312010-01-28 21:10:52 -0500187 } // if/else
188 } else {
189 cout << "Unable to open the disk for synchronization operation! The computer will\n"
190 << "continue to use the old partition table until you reboot or remove and\n"
191 << "re-insert the disk!\n";
srs5694add79a62010-01-26 15:59:58 -0500192 } // if (isOpen)
srs5694a17fe692011-09-10 20:30:20 -0400193 return retval;
srs5694add79a62010-01-26 15:59:58 -0500194} // DiskIO::DiskSync()
195
196// Seek to the specified sector. Returns 1 on success, 0 on failure.
197int DiskIO::Seek(uint64_t sector) {
198 int retval = 1;
199 LARGE_INTEGER seekTo;
srs5694add79a62010-01-26 15:59:58 -0500200
201 // If disk isn't open, try to open it....
202 if (!isOpen) {
203 retval = OpenForRead();
204 } // if
205
206 if (isOpen) {
srs56940a697312010-01-28 21:10:52 -0500207 seekTo.QuadPart = sector * (uint64_t) GetBlockSize();
srs5694add79a62010-01-26 15:59:58 -0500208 retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN);
209 if (retval == 0) {
210 errno = GetLastError();
srs56940a697312010-01-28 21:10:52 -0500211 cerr << "Error when seeking to " << seekTo.QuadPart << "! Error is " << errno << "\n";
srs5694add79a62010-01-26 15:59:58 -0500212 retval = 0;
213 } // if
214 } // if
215 return retval;
216} // DiskIO::Seek()
217
218// A variant on the standard read() function. Done to work around
219// limitations in FreeBSD concerning the matching of the sector
220// size with the number of bytes read.
221// Returns the number of bytes read into buffer.
222int DiskIO::Read(void* buffer, int numBytes) {
223 int blockSize = 512, i, numBlocks;
224 char* tempSpace;
225 DWORD retval = 0;
226
227 // If disk isn't open, try to open it....
228 if (!isOpen) {
229 OpenForRead();
230 } // if
231
232 if (isOpen) {
233 // Compute required space and allocate memory
234 blockSize = GetBlockSize();
235 if (numBytes <= blockSize) {
236 numBlocks = 1;
srs5694cb76c672010-02-11 22:22:22 -0500237 tempSpace = new char [blockSize];
srs5694add79a62010-01-26 15:59:58 -0500238 } else {
239 numBlocks = numBytes / blockSize;
srs5694cb76c672010-02-11 22:22:22 -0500240 if ((numBytes % blockSize) != 0)
241 numBlocks++;
242 tempSpace = new char [numBlocks * blockSize];
srs5694add79a62010-01-26 15:59:58 -0500243 } // if/else
srs56946aae2a92011-06-10 01:16:51 -0400244 if (tempSpace == NULL) {
245 cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n";
246 exit(1);
247 } // if
srs569434882942012-03-23 12:49:15 -0400248
srs5694add79a62010-01-26 15:59:58 -0500249 // Read the data into temporary space, then copy it to buffer
srs5694add79a62010-01-26 15:59:58 -0500250 ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL);
srs5694add79a62010-01-26 15:59:58 -0500251 for (i = 0; i < numBytes; i++) {
252 ((char*) buffer)[i] = tempSpace[i];
253 } // for
254
255 // Adjust the return value, if necessary....
256 if (((numBlocks * blockSize) != numBytes) && (retval > 0))
257 retval = numBytes;
258
srs5694cb76c672010-02-11 22:22:22 -0500259 delete[] tempSpace;
srs5694add79a62010-01-26 15:59:58 -0500260 } // if (isOpen)
261 return retval;
262} // DiskIO::Read()
263
srs56944307ef22012-05-30 12:30:48 -0400264// A variant on the standard write() function.
srs5694add79a62010-01-26 15:59:58 -0500265// Returns the number of bytes written.
266int DiskIO::Write(void* buffer, int numBytes) {
267 int blockSize = 512, i, numBlocks, retval = 0;
268 char* tempSpace;
269 DWORD numWritten;
270
271 // If disk isn't open, try to open it....
272 if ((!isOpen) || (!openForWrite)) {
273 OpenForWrite();
274 } // if
275
276 if (isOpen) {
277 // Compute required space and allocate memory
278 blockSize = GetBlockSize();
279 if (numBytes <= blockSize) {
280 numBlocks = 1;
srs5694cb76c672010-02-11 22:22:22 -0500281 tempSpace = new char [blockSize];
srs5694add79a62010-01-26 15:59:58 -0500282 } else {
283 numBlocks = numBytes / blockSize;
284 if ((numBytes % blockSize) != 0) numBlocks++;
srs5694cb76c672010-02-11 22:22:22 -0500285 tempSpace = new char [numBlocks * blockSize];
srs5694add79a62010-01-26 15:59:58 -0500286 } // if/else
srs56946aae2a92011-06-10 01:16:51 -0400287 if (tempSpace == NULL) {
288 cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n";
289 exit(1);
290 } // if
srs5694add79a62010-01-26 15:59:58 -0500291
292 // Copy the data to my own buffer, then write it
293 for (i = 0; i < numBytes; i++) {
294 tempSpace[i] = ((char*) buffer)[i];
295 } // for
296 for (i = numBytes; i < numBlocks * blockSize; i++) {
297 tempSpace[i] = 0;
298 } // for
srs5694add79a62010-01-26 15:59:58 -0500299 WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL);
300 retval = (int) numWritten;
301
302 // Adjust the return value, if necessary....
303 if (((numBlocks * blockSize) != numBytes) && (retval > 0))
304 retval = numBytes;
305
srs5694cb76c672010-02-11 22:22:22 -0500306 delete[] tempSpace;
srs5694add79a62010-01-26 15:59:58 -0500307 } // if (isOpen)
308 return retval;
309} // DiskIO:Write()
310
311// Returns the size of the disk in blocks.
312uint64_t DiskIO::DiskSize(int *err) {
313 uint64_t sectors = 0; // size in sectors
srs56940a697312010-01-28 21:10:52 -0500314 DWORD bytes, moreBytes; // low- and high-order bytes of file size
srs5694add79a62010-01-26 15:59:58 -0500315 GET_LENGTH_INFORMATION buf;
316 DWORD i;
317
318 // If disk isn't open, try to open it....
319 if (!isOpen) {
320 OpenForRead();
321 } // if
322
323 if (isOpen) {
324 // Note to self: I recall testing a simplified version of
325 // this code, similar to what's in the __APPLE__ block,
326 // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
327 // systems but not on 64-bit. Keep this in mind in case of
328 // 32/64-bit issues on MacOS....
srs5694add79a62010-01-26 15:59:58 -0500329 if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) {
330 sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize();
srs56940a697312010-01-28 21:10:52 -0500331 *err = 0;
332 } else { // doesn't seem to be a disk device; assume it's an image file....
333 bytes = GetFileSize(fd, &moreBytes);
334 sectors = ((uint64_t) bytes + ((uint64_t) moreBytes) * UINT32_MAX) / GetBlockSize();
335 *err = 0;
336 } // if
337 } else {
338 *err = -1;
339 sectors = 0;
340 } // if/else (isOpen)
srs5694add79a62010-01-26 15:59:58 -0500341
srs5694add79a62010-01-26 15:59:58 -0500342 return sectors;
343} // DiskIO::DiskSize()