blob: e5b1860eb85f10e956d6deb6e846d8bf879a6123 [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)) {
37 printf(prompt);
38 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)) {
86 printf(prompt);
87 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....
208 if (errno != ENOTTY) {
209 printf("\aError %d when determining sector size! Setting sector size to %d\n",
210 errno, SECTOR_SIZE);
211 } // if
212 } // if
213
srs5694e7b4ff92009-08-18 13:16:10 -0400214 if (result != 512) {
srs56942a9f5da2009-08-26 00:48:01 -0400215 printf("\aWARNING! Sector size is not 512 bytes! This program is likely to ");
216 printf("misbehave!\nProceed at your own risk!\n\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400217 } // if
218
srs5694e7b4ff92009-08-18 13:16:10 -0400219 return (result);
220} // GetBlockSize()
221
srs56942a9f5da2009-08-26 00:48:01 -0400222// Return a plain-text name for a partition type.
srs5694e7b4ff92009-08-18 13:16:10 -0400223// Convert a GUID to a string representation, suitable for display
224// to humans....
225char* GUIDToStr(struct GUIDData theGUID, char* theString) {
226 uint64_t block;
227
228 if (theString != NULL) {
229 block = (theGUID.data1 & UINT64_C(0x00000000FFFFFFFF));
230 sprintf(theString, "%08llX-", (unsigned long long) block);
231 block = (theGUID.data1 & UINT64_C(0x0000FFFF00000000)) >> 32;
232 sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
233 block = (theGUID.data1 & UINT64_C(0xFFFF000000000000)) >> 48;
234 sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
235 block = (theGUID.data2 & UINT64_C(0x00000000000000FF));
236 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
237 block = (theGUID.data2 & UINT64_C(0x000000000000FF00)) >> 8;
238 sprintf(theString, "%s%02llX-", theString, (unsigned long long) block);
239 block = (theGUID.data2 & UINT64_C(0x0000000000FF0000)) >> 16;
240 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
241 block = (theGUID.data2 & UINT64_C(0x00000000FF000000)) >> 24;
242 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
243 block = (theGUID.data2 & UINT64_C(0x000000FF00000000)) >> 32;
244 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
245 block = (theGUID.data2 & UINT64_C(0x0000FF0000000000)) >> 40;
246 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
247 block = (theGUID.data2 & UINT64_C(0x00FF000000000000)) >> 48;
248 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
249 block = (theGUID.data2 & UINT64_C(0xFF00000000000000)) >> 56;
250 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
251 } // if
252 return theString;
253} // GUIDToStr()
254
255// Get a GUID from the user
256GUIDData GetGUID(void) {
257 uint64_t part1, part2, part3, part4, part5;
258 int entered = 0;
259 char temp[255], temp2[255];
260 GUIDData theGUID;
261
262 printf("\nA GUID is entered in five segments of from two to six bytes, with\n"
263 "dashes between segments.\n");
264 printf("Enter the entire GUID, a four-byte hexadecimal number for the first segment, or\n"
265 "'R' to generate the entire GUID randomly: ");
266 fgets(temp, 255, stdin);
267
268 // If user entered 'r' or 'R', generate GUID randomly....
269 if ((temp[0] == 'r') || (temp[0] == 'R')) {
270 theGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
271 theGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
272 entered = 1;
273 } // if user entered 'R' or 'r'
274
275 // If string length is right for whole entry, try to parse it....
276 if ((strlen(temp) == 37) && (entered == 0)) {
277 strncpy(temp2, &temp[0], 8);
278 temp2[8] = '\0';
279 sscanf(temp2, "%llx", &part1);
280 strncpy(temp2, &temp[9], 4);
281 temp2[4] = '\0';
282 sscanf(temp2, "%llx", &part2);
283 strncpy(temp2, &temp[14], 4);
284 temp2[4] = '\0';
285 sscanf(temp2, "%llx", &part3);
286 theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
287 strncpy(temp2, &temp[19], 4);
288 temp2[4] = '\0';
289 sscanf(temp2, "%llx", &part4);
290 strncpy(temp2, &temp[24], 12);
291 temp2[12] = '\0';
292 sscanf(temp2, "%llx", &part5);
293 theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
294 ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
295 ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
296 ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
297 ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
298 ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
299 ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
300 ((part5 & UINT64_C(0x00000000000000FF)) << 56);
301 entered = 1;
302 } // if
303
304 // If neither of the above methods of entry was used, use prompted
305 // entry....
306 if (entered == 0) {
307 sscanf(temp, "%llx", &part1);
308 printf("Enter a two-byte hexadecimal number for the second segment: ");
309 fgets(temp, 255, stdin);
310 sscanf(temp, "%llx", &part2);
311 printf("Enter a two-byte hexadecimal number for the third segment: ");
312 fgets(temp, 255, stdin);
313 sscanf(temp, "%llx", &part3);
314 theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
315 printf("Enter a two-byte hexadecimal number for the fourth segment: ");
316 fgets(temp, 255, stdin);
317 sscanf(temp, "%llx", &part4);
318 printf("Enter a six-byte hexadecimal number for the fifth segment: ");
319 fgets(temp, 255, stdin);
320 sscanf(temp, "%llx", &part5);
321 theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
322 ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
323 ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
324 ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
325 ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
326 ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
327 ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
328 ((part5 & UINT64_C(0x00000000000000FF)) << 56);
329 entered = 1;
330 } // if/else
331 printf("New GUID: %s\n", GUIDToStr(theGUID, temp));
332 return theGUID;
333} // GetGUID()
334
srs56942a9f5da2009-08-26 00:48:01 -0400335// Return 1 if the CPU architecture is little endian, 0 if it's big endian....
336int IsLittleEndian(void) {
337 int littleE = 1; // assume little-endian (Intel-style)
338 union {
339 uint32_t num;
340 unsigned char uc[sizeof(uint32_t)];
341 } endian;
342
343 endian.num = 1;
344 if (endian.uc[0] != (unsigned char) 1) {
345 littleE = 0;
346 } // if
347 return (littleE);
348} // IsLittleEndian()
349
350// Reverse the byte order of theValue; numBytes is number of bytes
srs5694221e0872009-08-29 15:00:31 -0400351void ReverseBytes(void* theValue, int numBytes) {
352 char* origValue;
srs56942a9f5da2009-08-26 00:48:01 -0400353 char* tempValue;
354 int i;
355
srs5694221e0872009-08-29 15:00:31 -0400356 origValue = (char*) theValue;
srs56942a9f5da2009-08-26 00:48:01 -0400357 tempValue = (char*) malloc(numBytes);
358 for (i = 0; i < numBytes; i++)
srs5694221e0872009-08-29 15:00:31 -0400359 tempValue[i] = origValue[i];
srs56942a9f5da2009-08-26 00:48:01 -0400360 for (i = 0; i < numBytes; i++)
srs5694221e0872009-08-29 15:00:31 -0400361 origValue[i] = tempValue[numBytes - i - 1];
srs56942a9f5da2009-08-26 00:48:01 -0400362 free(tempValue);
363} // ReverseBytes()
364
srs5694e7b4ff92009-08-18 13:16:10 -0400365// Compute (2 ^ value). Given the return type, value must be 63 or less.
366// Used in some bit-fiddling functions
367uint64_t PowerOf2(int value) {
368 uint64_t retval = 1;
369 int i;
370
371 if ((value < 64) && (value >= 0)) {
372 for (i = 0; i < value; i++) {
373 retval *= 2;
374 } // for
375 } else retval = 0;
376 return retval;
377} // PowerOf2()
378
srs5694e4ac11e2009-08-31 10:13:04 -0400379// An extended file-open function. This includes some system-specific checks.
380// I want them in a function because I use these calls twice and I don't want
381// to forget to change them in one location if I need to change them in
382// the other....
383int OpenForWrite(char* deviceFilename) {
384 int fd;
385
386 fd = open(deviceFilename, O_WRONLY); // try to open the device; may fail....
387#ifdef __APPLE__
388 // MacOS X requires a shared lock under some circumstances....
389 if (fd < 0) {
390 fd = open(deviceFilename, O_WRONLY|O_SHLOCK);
391 } // if
392#endif
srs5694e35eb1b2009-09-14 00:29:34 -0400393 return fd;
394} // OpenForWrite()
395
396// Resync disk caches so the OS uses the new partition table. This code varies
397// a lot from one OS to another.
398void DiskSync(int fd) {
399 int i;
400
401 sync();
402#ifdef __APPLE__
403 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
404 "You should reboot or remove the drive.\n");
405 /* don't know if this helps
406 * it definitely will get things on disk though:
407 * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
408 i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
409#else
410#ifdef __FreeBSD__
411 sleep(2);
412 i = ioctl(fd, DIOCGFLUSH);
413 printf("Warning: The kernel may continue to use old or deleted partitions.\n"
414 "You should reboot or remove the drive.\n");
415#else
416 sleep(2);
417 i = ioctl(fd, BLKRRPART);
418 if (i)
419 printf("Warning: The kernel is still using the old partition table.\n"
420 "The new table will be used at the next reboot.\n");
421#endif
422#endif
423} // DiskSync()
srs5694221e0872009-08-29 15:00:31 -0400424
srs5694e7b4ff92009-08-18 13:16:10 -0400425/**************************************************************************************
426 * *
427 * Below functions are lifted from various sources, as documented in comments before *
428 * each one. *
429 * *
430 **************************************************************************************/
431
432// The disksize function is taken from the Linux fdisk code and modified
433// to work around a problem returning a uint64_t value on Mac OS.
434uint64_t disksize(int fd, int *err) {
srs5694e35eb1b2009-09-14 00:29:34 -0400435 long sz; // Do not delete; needed for Linux
436 long long b; // Do not delete; needed for Linux
437 uint64_t sectors = 0, bytes = 0; // size in sectors & bytes
438 struct stat st;
srs5694e7b4ff92009-08-18 13:16:10 -0400439
srs5694e35eb1b2009-09-14 00:29:34 -0400440 // Note to self: I recall testing a simplified version of
441 // this code, similar to what's in the __APPLE__ block,
442 // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
443 // systems but not on 64-bit. Keep this in mind in case of
444 // 32/64-bit issues on MacOS....
srs5694e7b4ff92009-08-18 13:16:10 -0400445#ifdef __APPLE__
srs5694e35eb1b2009-09-14 00:29:34 -0400446 *err = ioctl(fd, DKIOCGETBLOCKCOUNT, &sectors);
srs5694e7b4ff92009-08-18 13:16:10 -0400447#else
srs5694221e0872009-08-29 15:00:31 -0400448#ifdef __FreeBSD__
srs5694e35eb1b2009-09-14 00:29:34 -0400449 *err = ioctl(fd, DIOCGMEDIASIZE, &sz);
450 b = GetBlockSize(fd);
451 sectors = sz / b;
srs5694221e0872009-08-29 15:00:31 -0400452#else
srs5694e35eb1b2009-09-14 00:29:34 -0400453 *err = ioctl(fd, BLKGETSIZE, &sz);
454 if (*err) {
455 sectors = sz = 0;
456 } // if
457 if ((errno == EFBIG) || (!*err)) {
458 *err = ioctl(fd, BLKGETSIZE64, &b);
459 if (*err || b == 0 || b == sz)
460 sectors = sz;
461 else
462 sectors = (b >> 9);
463 } // if
464
465// if (*err) {
466// sz = 0;
467// if (errno != EFBIG)
468// return sz;
469// }
470// *err = ioctl(fd, BLKGETSIZE64, &b);
471// if (*err || b == 0 || b == sz)
472// sectors = sz;
473// else
474// sectors = (b >> 9);
475
srs5694e7b4ff92009-08-18 13:16:10 -0400476#endif
srs5694221e0872009-08-29 15:00:31 -0400477#endif
srs5694e35eb1b2009-09-14 00:29:34 -0400478
479 // The above methods have failed (or it's a bum filename reference),
480 // so let's assume it's a regular file (a QEMU image, dd backup, or
481 // what have you) and see what stat() gives us....
482 if (sectors == 0) {
483 if (fstat(fd, &st) == 0) {
484 bytes = (uint64_t) st.st_size;
485 if ((bytes % UINT64_C(512)) != 0)
486 fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!"
487 " Misbehavior is likely!\n\a");
488 sectors = bytes / UINT64_C(512);
489 } // if
490 } // if
491// printf("In disksize(), sectors is %lld.\n", sectors);
492 return sectors;
srs5694e7b4ff92009-08-18 13:16:10 -0400493}