blob: 090261784a168c1a0456c221032d147cd3887591 [file] [log] [blame]
srs5694e7b4ff92009-08-18 13:16:10 -04001// support.cc
2// Non-class support functions for gdisk program.
3// Primarily by Rod Smith, February 2009, but with a few functions
4// copied from other sources (see attributions below).
5
srs5694221e0872009-08-29 15:00:31 -04006/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
7 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
srs5694e7b4ff92009-08-18 13:16:10 -04009#define __STDC_LIMIT_MACROS
10#define __STDC_CONSTANT_MACROS
11
12#include <sys/ioctl.h>
13#include <stdio.h>
14#include <string.h>
15#include <stdint.h>
16#include <errno.h>
srs5694e4ac11e2009-08-31 10:13:04 -040017#include <fcntl.h>
srs5694e35eb1b2009-09-14 00:29:34 -040018#include <sys/stat.h>
srs5694e7b4ff92009-08-18 13:16:10 -040019#include "support.h"
20
21#include <sys/types.h>
22
23using namespace std;
24
25// Get a numeric value from the user, between low and high (inclusive).
26// Keeps looping until the user enters a value within that range.
27// If user provides no input, def (default value) is returned.
28// (If def is outside of the low-high range, an explicit response
29// is required.)
30int GetNumber(int low, int high, int def, const char prompt[]) {
31 int response, num;
32 char line[255];
33
34 if (low != high) { // bother only if low and high differ...
35 response = low - 1; // force one loop by setting response outside range
36 while ((response < low) || (response > high)) {
srs56941d1448a2009-12-31 21:20:19 -050037 printf("%s", prompt);
srs5694e7b4ff92009-08-18 13:16:10 -040038 fgets(line, 255, stdin);
39 num = sscanf(line, "%d", &response);
40 if (num == 1) { // user provided a response
41 if ((response < low) || (response > high))
42 printf("Value out of range\n");
43 } else { // user hit enter; return default
44 response = def;
45 } // if/else
46 } // while
47 } else { // low == high, so return this value
48 printf("Using %d\n", low);
49 response = low;
50 } // else
51 return (response);
52} // GetNumber()
53
54// Gets a Y/N response (and converts lowercase to uppercase)
55char GetYN(void) {
56 char line[255];
57 char response = '\0';
58
59 while ((response != 'Y') && (response != 'N')) {
60 printf("(Y/N): ");
61 fgets(line, 255, stdin);
62 sscanf(line, "%c", &response);
63 if (response == 'y') response = 'Y';
64 if (response == 'n') response = 'N';
65 } // while
66 return response;
67} // GetYN(void)
68
srs5694e4ac11e2009-08-31 10:13:04 -040069// Obtains a sector number, between low and high, from the
srs5694e7b4ff92009-08-18 13:16:10 -040070// user, accepting values prefixed by "+" to add sectors to low,
71// or the same with "K", "M", "G", or "T" as suffixes to add
72// kilobytes, megabytes, gigabytes, or terabytes, respectively.
srs5694e4ac11e2009-08-31 10:13:04 -040073// If a "-" prefix is used, use the high value minus the user-
74// specified number of sectors (or KiB, MiB, etc.). Use the def
75 //value as the default if the user just hits Enter
76uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, char prompt[]) {
srs5694e7b4ff92009-08-18 13:16:10 -040077 unsigned long long response;
78 int num;
79 int plusFlag = 0;
80 uint64_t mult = 1;
81 char suffix;
82 char line[255];
83
84 response = low - 1; // Ensure one pass by setting a too-low initial value
85 while ((response < low) || (response > high)) {
srs56941d1448a2009-12-31 21:20:19 -050086 printf("%s", prompt);
srs5694e7b4ff92009-08-18 13:16:10 -040087 fgets(line, 255, stdin);
88
89 // Remove leading spaces, if present
90 while (line[0] == ' ')
91 strcpy(line, &line[1]);
92
93 // If present, flag and remove leading plus sign
94 if (line[0] == '+') {
95 plusFlag = 1;
96 strcpy(line, &line[1]);
97 } // if
98
srs5694e4ac11e2009-08-31 10:13:04 -040099 // If present, flag and remove leading minus sign
100 if (line[0] == '-') {
101 plusFlag = -1;
102 strcpy(line, &line[1]);
103 } // if
104
srs5694e7b4ff92009-08-18 13:16:10 -0400105 // Extract numeric response and, if present, suffix
106 num = sscanf(line, "%llu%c", &response, &suffix);
107
srs5694e4ac11e2009-08-31 10:13:04 -0400108 // If no response, use default (def)
srs5694e7b4ff92009-08-18 13:16:10 -0400109 if (num <= 0) {
srs5694e4ac11e2009-08-31 10:13:04 -0400110 response = (unsigned long long) def;
srs5694e7b4ff92009-08-18 13:16:10 -0400111 suffix = ' ';
srs5694e19ba092009-08-24 14:10:35 -0400112 plusFlag = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400113 } // if
114
115 // Set multiplier based on suffix
116 switch (suffix) {
117 case 'K':
118 case 'k':
119 mult = (uint64_t) 1024 / SECTOR_SIZE;
120 break;
121 case 'M':
122 case 'm':
123 mult = (uint64_t) 1048576 / SECTOR_SIZE;
124 break;
125 case 'G':
126 case 'g':
127 mult = (uint64_t) 1073741824 / SECTOR_SIZE;
128 break;
129 case 'T':
130 case 't':
131 mult = ((uint64_t) 1073741824 * (uint64_t) 1024) / (uint64_t) SECTOR_SIZE;
132 break;
133 default:
134 mult = 1;
135 } // switch
136
137 // Adjust response based on multiplier and plus flag, if present
138 response *= (unsigned long long) mult;
139 if (plusFlag == 1) {
srs5694e4ac11e2009-08-31 10:13:04 -0400140 response = response + (unsigned long long) low - UINT64_C(1);
141 } // if
142 if (plusFlag == -1) {
143 response = (unsigned long long) high - response;
srs5694e19ba092009-08-24 14:10:35 -0400144 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400145 } // while
146 return ((uint64_t) response);
srs5694e4ac11e2009-08-31 10:13:04 -0400147} // GetSectorNum()
srs5694e7b4ff92009-08-18 13:16:10 -0400148
srs5694e7b4ff92009-08-18 13:16:10 -0400149// Takes a size in bytes (in size) and converts this to a size in
150// SI units (KiB, MiB, GiB, TiB, or PiB), returned in C++ string
151// form
152char* BytesToSI(uint64_t size, char theValue[]) {
153 char units[8];
154 float sizeInSI;
155
156 if (theValue != NULL) {
157 sizeInSI = (float) size;
158 strcpy (units, " bytes");
159 if (sizeInSI > 1024.0) {
160 sizeInSI /= 1024.0;
161 strcpy(units, " KiB");
162 } // if
163 if (sizeInSI > 1024.0) {
164 sizeInSI /= 1024.0;
165 strcpy(units, " MiB");
166 } // if
167 if (sizeInSI > 1024.0) {
168 sizeInSI /= 1024.0;
169 strcpy(units, " GiB");
170 } // if
171 if (sizeInSI > 1024.0) {
172 sizeInSI /= 1024.0;
173 strcpy(units, " TiB");
174 } // if
175 if (sizeInSI > 1024.0) {
176 sizeInSI /= 1024.0;
177 strcpy(units, " PiB");
178 } // if
179 if (strcmp(units, " bytes") == 0) { // in bytes, so no decimal point
180 sprintf(theValue, "%1.0f%s", sizeInSI, units);
181 } else {
182 sprintf(theValue, "%1.1f%s", sizeInSI, units);
183 } // if/else
184 } // if
185 return theValue;
186} // BlocksToSI()
187
srs5694e35eb1b2009-09-14 00:29:34 -0400188// Returns block size of device pointed to by fd file descriptor. If the ioctl
189// returns an error condition, print a warning but return a value of SECTOR_SIZE
190// (512)..
srs5694e7b4ff92009-08-18 13:16:10 -0400191int GetBlockSize(int fd) {
192 int err, result;
193
194#ifdef __APPLE__
195 err = ioctl(fd, DKIOCGETBLOCKSIZE, &result);
196#else
srs5694221e0872009-08-29 15:00:31 -0400197#ifdef __FreeBSD__
198 err = ioctl(fd, DIOCGSECTORSIZE, &result);
199#else
srs5694e7b4ff92009-08-18 13:16:10 -0400200 err = ioctl(fd, BLKSSZGET, &result);
201#endif
srs5694221e0872009-08-29 15:00:31 -0400202#endif
srs5694e7b4ff92009-08-18 13:16:10 -0400203
srs5694e35eb1b2009-09-14 00:29:34 -0400204 if (err == -1) {
205 result = SECTOR_SIZE;
206 // ENOTTY = inappropriate ioctl; probably being called on a disk image
207 // file, so don't display the warning message....
srs5694978041c2009-09-21 20:51:47 -0400208 // 32-bit code returns EINVAL, I don't know why. I know I'm treading on
209 // thin ice here, but it should be OK in all but very weird cases....
210 if ((errno != ENOTTY) && (errno != EINVAL)) {
srs5694e35eb1b2009-09-14 00:29:34 -0400211 printf("\aError %d when determining sector size! Setting sector size to %d\n",
212 errno, SECTOR_SIZE);
213 } // if
214 } // if
215
srs5694e7b4ff92009-08-18 13:16:10 -0400216 if (result != 512) {
srs56942a9f5da2009-08-26 00:48:01 -0400217 printf("\aWARNING! Sector size is not 512 bytes! This program is likely to ");
218 printf("misbehave!\nProceed at your own risk!\n\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400219 } // if
220
srs5694e7b4ff92009-08-18 13:16:10 -0400221 return (result);
222} // GetBlockSize()
223
srs56942a9f5da2009-08-26 00:48:01 -0400224// Return a plain-text name for a partition type.
srs5694e7b4ff92009-08-18 13:16:10 -0400225// Convert a GUID to a string representation, suitable for display
226// to humans....
227char* GUIDToStr(struct GUIDData theGUID, char* theString) {
228 uint64_t block;
229
230 if (theString != NULL) {
231 block = (theGUID.data1 & UINT64_C(0x00000000FFFFFFFF));
232 sprintf(theString, "%08llX-", (unsigned long long) block);
233 block = (theGUID.data1 & UINT64_C(0x0000FFFF00000000)) >> 32;
234 sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
235 block = (theGUID.data1 & UINT64_C(0xFFFF000000000000)) >> 48;
236 sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
237 block = (theGUID.data2 & UINT64_C(0x00000000000000FF));
238 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
239 block = (theGUID.data2 & UINT64_C(0x000000000000FF00)) >> 8;
240 sprintf(theString, "%s%02llX-", theString, (unsigned long long) block);
241 block = (theGUID.data2 & UINT64_C(0x0000000000FF0000)) >> 16;
242 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
243 block = (theGUID.data2 & UINT64_C(0x00000000FF000000)) >> 24;
244 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
245 block = (theGUID.data2 & UINT64_C(0x000000FF00000000)) >> 32;
246 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
247 block = (theGUID.data2 & UINT64_C(0x0000FF0000000000)) >> 40;
248 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
249 block = (theGUID.data2 & UINT64_C(0x00FF000000000000)) >> 48;
250 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
251 block = (theGUID.data2 & UINT64_C(0xFF00000000000000)) >> 56;
252 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
253 } // if
254 return theString;
255} // GUIDToStr()
256
257// Get a GUID from the user
258GUIDData GetGUID(void) {
259 uint64_t part1, part2, part3, part4, part5;
260 int entered = 0;
261 char temp[255], temp2[255];
262 GUIDData theGUID;
263
264 printf("\nA GUID is entered in five segments of from two to six bytes, with\n"
265 "dashes between segments.\n");
266 printf("Enter the entire GUID, a four-byte hexadecimal number for the first segment, or\n"
267 "'R' to generate the entire GUID randomly: ");
268 fgets(temp, 255, stdin);
269
270 // If user entered 'r' or 'R', generate GUID randomly....
271 if ((temp[0] == 'r') || (temp[0] == 'R')) {
272 theGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
273 theGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
274 entered = 1;
275 } // if user entered 'R' or 'r'
276
277 // If string length is right for whole entry, try to parse it....
278 if ((strlen(temp) == 37) && (entered == 0)) {
279 strncpy(temp2, &temp[0], 8);
280 temp2[8] = '\0';
281 sscanf(temp2, "%llx", &part1);
282 strncpy(temp2, &temp[9], 4);
283 temp2[4] = '\0';
284 sscanf(temp2, "%llx", &part2);
285 strncpy(temp2, &temp[14], 4);
286 temp2[4] = '\0';
287 sscanf(temp2, "%llx", &part3);
288 theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
289 strncpy(temp2, &temp[19], 4);
290 temp2[4] = '\0';
291 sscanf(temp2, "%llx", &part4);
292 strncpy(temp2, &temp[24], 12);
293 temp2[12] = '\0';
294 sscanf(temp2, "%llx", &part5);
295 theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
296 ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
297 ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
298 ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
299 ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
300 ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
301 ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
302 ((part5 & UINT64_C(0x00000000000000FF)) << 56);
303 entered = 1;
304 } // if
305
306 // If neither of the above methods of entry was used, use prompted
307 // entry....
308 if (entered == 0) {
309 sscanf(temp, "%llx", &part1);
310 printf("Enter a two-byte hexadecimal number for the second segment: ");
311 fgets(temp, 255, stdin);
312 sscanf(temp, "%llx", &part2);
313 printf("Enter a two-byte hexadecimal number for the third segment: ");
314 fgets(temp, 255, stdin);
315 sscanf(temp, "%llx", &part3);
316 theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
317 printf("Enter a two-byte hexadecimal number for the fourth segment: ");
318 fgets(temp, 255, stdin);
319 sscanf(temp, "%llx", &part4);
320 printf("Enter a six-byte hexadecimal number for the fifth segment: ");
321 fgets(temp, 255, stdin);
322 sscanf(temp, "%llx", &part5);
323 theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
324 ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
325 ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
326 ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
327 ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
328 ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
329 ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
330 ((part5 & UINT64_C(0x00000000000000FF)) << 56);
331 entered = 1;
332 } // if/else
333 printf("New GUID: %s\n", GUIDToStr(theGUID, temp));
334 return theGUID;
335} // GetGUID()
336
srs56942a9f5da2009-08-26 00:48:01 -0400337// Return 1 if the CPU architecture is little endian, 0 if it's big endian....
338int IsLittleEndian(void) {
339 int littleE = 1; // assume little-endian (Intel-style)
340 union {
341 uint32_t num;
342 unsigned char uc[sizeof(uint32_t)];
343 } endian;
344
345 endian.num = 1;
346 if (endian.uc[0] != (unsigned char) 1) {
347 littleE = 0;
348 } // if
349 return (littleE);
350} // IsLittleEndian()
351
352// Reverse the byte order of theValue; numBytes is number of bytes
srs5694221e0872009-08-29 15:00:31 -0400353void ReverseBytes(void* theValue, int numBytes) {
354 char* origValue;
srs56942a9f5da2009-08-26 00:48:01 -0400355 char* tempValue;
356 int i;
357
srs5694221e0872009-08-29 15:00:31 -0400358 origValue = (char*) theValue;
srs56942a9f5da2009-08-26 00:48:01 -0400359 tempValue = (char*) malloc(numBytes);
360 for (i = 0; i < numBytes; i++)
srs5694221e0872009-08-29 15:00:31 -0400361 tempValue[i] = origValue[i];
srs56942a9f5da2009-08-26 00:48:01 -0400362 for (i = 0; i < numBytes; i++)
srs5694221e0872009-08-29 15:00:31 -0400363 origValue[i] = tempValue[numBytes - i - 1];
srs56942a9f5da2009-08-26 00:48:01 -0400364 free(tempValue);
365} // ReverseBytes()
366
srs5694e7b4ff92009-08-18 13:16:10 -0400367// Compute (2 ^ value). Given the return type, value must be 63 or less.
368// Used in some bit-fiddling functions
369uint64_t PowerOf2(int value) {
370 uint64_t retval = 1;
371 int i;
372
373 if ((value < 64) && (value >= 0)) {
374 for (i = 0; i < value; i++) {
375 retval *= 2;
376 } // for
377 } else retval = 0;
378 return retval;
379} // PowerOf2()
380
srs5694e4ac11e2009-08-31 10:13:04 -0400381// An extended file-open function. This includes some system-specific checks.
382// I want them in a function because I use these calls twice and I don't want
383// to forget to change them in one location if I need to change them in
384// the other....
385int OpenForWrite(char* deviceFilename) {
386 int fd;
387
388 fd = open(deviceFilename, O_WRONLY); // try to open the device; may fail....
389#ifdef __APPLE__
390 // MacOS X requires a shared lock under some circumstances....
391 if (fd < 0) {
392 fd = open(deviceFilename, O_WRONLY|O_SHLOCK);
393 } // if
394#endif
srs5694e35eb1b2009-09-14 00:29:34 -0400395 return fd;
396} // OpenForWrite()
397
398// Resync disk caches so the OS uses the new partition table. This code varies
399// a lot from one OS to another.
400void DiskSync(int fd) {
401 int i;
402
403 sync();
404#ifdef __APPLE__
405 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
406 "You should reboot or remove the drive.\n");
407 /* don't know if this helps
408 * it definitely will get things on disk though:
409 * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
410 i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
411#else
412#ifdef __FreeBSD__
413 sleep(2);
414 i = ioctl(fd, DIOCGFLUSH);
415 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
416 "You should reboot or remove the drive.\n");
417#else
418 sleep(2);
419 i = ioctl(fd, BLKRRPART);
420 if (i)
421 printf("Warning: The kernel is still using the old partition table.\n"
422 "The new table will be used at the next reboot.\n");
423#endif
424#endif
425} // DiskSync()
srs5694221e0872009-08-29 15:00:31 -0400426
srs5694e7b4ff92009-08-18 13:16:10 -0400427/**************************************************************************************
428 * *
429 * Below functions are lifted from various sources, as documented in comments before *
430 * each one. *
431 * *
432 **************************************************************************************/
433
434// The disksize function is taken from the Linux fdisk code and modified
435// to work around a problem returning a uint64_t value on Mac OS.
436uint64_t disksize(int fd, int *err) {
srs5694e35eb1b2009-09-14 00:29:34 -0400437 long sz; // Do not delete; needed for Linux
438 long long b; // Do not delete; needed for Linux
srs5694978041c2009-09-21 20:51:47 -0400439 uint64_t sectors = 0; // size in sectors
440 off_t bytes = 0; // size in bytes
441 struct stat64 st;
srs5694e7b4ff92009-08-18 13:16:10 -0400442
srs5694e35eb1b2009-09-14 00:29:34 -0400443 // Note to self: I recall testing a simplified version of
444 // this code, similar to what's in the __APPLE__ block,
445 // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
446 // systems but not on 64-bit. Keep this in mind in case of
447 // 32/64-bit issues on MacOS....
srs5694e7b4ff92009-08-18 13:16:10 -0400448#ifdef __APPLE__
srs5694e35eb1b2009-09-14 00:29:34 -0400449 *err = ioctl(fd, DKIOCGETBLOCKCOUNT, &sectors);
srs5694e7b4ff92009-08-18 13:16:10 -0400450#else
srs5694221e0872009-08-29 15:00:31 -0400451#ifdef __FreeBSD__
srs5694e35eb1b2009-09-14 00:29:34 -0400452 *err = ioctl(fd, DIOCGMEDIASIZE, &sz);
453 b = GetBlockSize(fd);
454 sectors = sz / b;
srs5694221e0872009-08-29 15:00:31 -0400455#else
srs5694e35eb1b2009-09-14 00:29:34 -0400456 *err = ioctl(fd, BLKGETSIZE, &sz);
457 if (*err) {
458 sectors = sz = 0;
459 } // if
460 if ((errno == EFBIG) || (!*err)) {
461 *err = ioctl(fd, BLKGETSIZE64, &b);
462 if (*err || b == 0 || b == sz)
463 sectors = sz;
464 else
465 sectors = (b >> 9);
466 } // if
467
srs5694e7b4ff92009-08-18 13:16:10 -0400468#endif
srs5694221e0872009-08-29 15:00:31 -0400469#endif
srs5694e35eb1b2009-09-14 00:29:34 -0400470
471 // The above methods have failed (or it's a bum filename reference),
472 // so let's assume it's a regular file (a QEMU image, dd backup, or
473 // what have you) and see what stat() gives us....
474 if (sectors == 0) {
srs5694978041c2009-09-21 20:51:47 -0400475 if (fstat64(fd, &st) == 0) {
srs5694e35eb1b2009-09-14 00:29:34 -0400476 bytes = (uint64_t) st.st_size;
477 if ((bytes % UINT64_C(512)) != 0)
478 fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!"
479 " Misbehavior is likely!\n\a");
480 sectors = bytes / UINT64_C(512);
481 } // if
482 } // if
srs5694247657a2009-11-26 18:36:12 -0500483// sectors = 25000000;
484// printf("Returning bogus sector size: %d\n", sectors);
srs5694e35eb1b2009-09-14 00:29:34 -0400485 return sectors;
srs5694e7b4ff92009-08-18 13:16:10 -0400486}