blob: 798aff8c6d228417ae303dbe051cd1ab348c057d [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>
srs5694e7b4ff92009-08-18 13:16:10 -040018#include "support.h"
19
20#include <sys/types.h>
21
22using namespace std;
23
24// Get a numeric value from the user, between low and high (inclusive).
25// Keeps looping until the user enters a value within that range.
26// If user provides no input, def (default value) is returned.
27// (If def is outside of the low-high range, an explicit response
28// is required.)
29int GetNumber(int low, int high, int def, const char prompt[]) {
30 int response, num;
31 char line[255];
32
33 if (low != high) { // bother only if low and high differ...
34 response = low - 1; // force one loop by setting response outside range
35 while ((response < low) || (response > high)) {
36 printf(prompt);
37 fgets(line, 255, stdin);
38 num = sscanf(line, "%d", &response);
39 if (num == 1) { // user provided a response
40 if ((response < low) || (response > high))
41 printf("Value out of range\n");
42 } else { // user hit enter; return default
43 response = def;
44 } // if/else
45 } // while
46 } else { // low == high, so return this value
47 printf("Using %d\n", low);
48 response = low;
49 } // else
50 return (response);
51} // GetNumber()
52
53// Gets a Y/N response (and converts lowercase to uppercase)
54char GetYN(void) {
55 char line[255];
56 char response = '\0';
57
58 while ((response != 'Y') && (response != 'N')) {
59 printf("(Y/N): ");
60 fgets(line, 255, stdin);
61 sscanf(line, "%c", &response);
62 if (response == 'y') response = 'Y';
63 if (response == 'n') response = 'N';
64 } // while
65 return response;
66} // GetYN(void)
67
srs5694e4ac11e2009-08-31 10:13:04 -040068// Obtains a sector number, between low and high, from the
srs5694e7b4ff92009-08-18 13:16:10 -040069// user, accepting values prefixed by "+" to add sectors to low,
70// or the same with "K", "M", "G", or "T" as suffixes to add
71// kilobytes, megabytes, gigabytes, or terabytes, respectively.
srs5694e4ac11e2009-08-31 10:13:04 -040072// If a "-" prefix is used, use the high value minus the user-
73// specified number of sectors (or KiB, MiB, etc.). Use the def
74 //value as the default if the user just hits Enter
75uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, char prompt[]) {
srs5694e7b4ff92009-08-18 13:16:10 -040076 unsigned long long response;
77 int num;
78 int plusFlag = 0;
79 uint64_t mult = 1;
80 char suffix;
81 char line[255];
82
83 response = low - 1; // Ensure one pass by setting a too-low initial value
84 while ((response < low) || (response > high)) {
85 printf(prompt);
86 fgets(line, 255, stdin);
87
88 // Remove leading spaces, if present
89 while (line[0] == ' ')
90 strcpy(line, &line[1]);
91
92 // If present, flag and remove leading plus sign
93 if (line[0] == '+') {
94 plusFlag = 1;
95 strcpy(line, &line[1]);
96 } // if
97
srs5694e4ac11e2009-08-31 10:13:04 -040098 // If present, flag and remove leading minus sign
99 if (line[0] == '-') {
100 plusFlag = -1;
101 strcpy(line, &line[1]);
102 } // if
103
srs5694e7b4ff92009-08-18 13:16:10 -0400104 // Extract numeric response and, if present, suffix
105 num = sscanf(line, "%llu%c", &response, &suffix);
106
srs5694e4ac11e2009-08-31 10:13:04 -0400107 // If no response, use default (def)
srs5694e7b4ff92009-08-18 13:16:10 -0400108 if (num <= 0) {
srs5694e4ac11e2009-08-31 10:13:04 -0400109 response = (unsigned long long) def;
srs5694e7b4ff92009-08-18 13:16:10 -0400110 suffix = ' ';
srs5694e19ba092009-08-24 14:10:35 -0400111 plusFlag = 0;
srs5694e7b4ff92009-08-18 13:16:10 -0400112 } // if
113
114 // Set multiplier based on suffix
115 switch (suffix) {
116 case 'K':
117 case 'k':
118 mult = (uint64_t) 1024 / SECTOR_SIZE;
119 break;
120 case 'M':
121 case 'm':
122 mult = (uint64_t) 1048576 / SECTOR_SIZE;
123 break;
124 case 'G':
125 case 'g':
126 mult = (uint64_t) 1073741824 / SECTOR_SIZE;
127 break;
128 case 'T':
129 case 't':
130 mult = ((uint64_t) 1073741824 * (uint64_t) 1024) / (uint64_t) SECTOR_SIZE;
131 break;
132 default:
133 mult = 1;
134 } // switch
135
136 // Adjust response based on multiplier and plus flag, if present
137 response *= (unsigned long long) mult;
138 if (plusFlag == 1) {
srs5694e4ac11e2009-08-31 10:13:04 -0400139 response = response + (unsigned long long) low - UINT64_C(1);
140 } // if
141 if (plusFlag == -1) {
142 response = (unsigned long long) high - response;
srs5694e19ba092009-08-24 14:10:35 -0400143 } // if
srs5694e7b4ff92009-08-18 13:16:10 -0400144 } // while
145 return ((uint64_t) response);
srs5694e4ac11e2009-08-31 10:13:04 -0400146} // GetSectorNum()
srs5694e7b4ff92009-08-18 13:16:10 -0400147
srs5694e7b4ff92009-08-18 13:16:10 -0400148// Takes a size in bytes (in size) and converts this to a size in
149// SI units (KiB, MiB, GiB, TiB, or PiB), returned in C++ string
150// form
151char* BytesToSI(uint64_t size, char theValue[]) {
152 char units[8];
153 float sizeInSI;
154
155 if (theValue != NULL) {
156 sizeInSI = (float) size;
157 strcpy (units, " bytes");
158 if (sizeInSI > 1024.0) {
159 sizeInSI /= 1024.0;
160 strcpy(units, " KiB");
161 } // if
162 if (sizeInSI > 1024.0) {
163 sizeInSI /= 1024.0;
164 strcpy(units, " MiB");
165 } // if
166 if (sizeInSI > 1024.0) {
167 sizeInSI /= 1024.0;
168 strcpy(units, " GiB");
169 } // if
170 if (sizeInSI > 1024.0) {
171 sizeInSI /= 1024.0;
172 strcpy(units, " TiB");
173 } // if
174 if (sizeInSI > 1024.0) {
175 sizeInSI /= 1024.0;
176 strcpy(units, " PiB");
177 } // if
178 if (strcmp(units, " bytes") == 0) { // in bytes, so no decimal point
179 sprintf(theValue, "%1.0f%s", sizeInSI, units);
180 } else {
181 sprintf(theValue, "%1.1f%s", sizeInSI, units);
182 } // if/else
183 } // if
184 return theValue;
185} // BlocksToSI()
186
187// Returns block size of device pointed to by fd file descriptor, or -1
188// if there's a problem
189int GetBlockSize(int fd) {
190 int err, result;
191
192#ifdef __APPLE__
193 err = ioctl(fd, DKIOCGETBLOCKSIZE, &result);
194#else
srs5694221e0872009-08-29 15:00:31 -0400195#ifdef __FreeBSD__
196 err = ioctl(fd, DIOCGSECTORSIZE, &result);
197#else
srs5694e7b4ff92009-08-18 13:16:10 -0400198 err = ioctl(fd, BLKSSZGET, &result);
199#endif
srs5694221e0872009-08-29 15:00:31 -0400200#endif
srs5694e7b4ff92009-08-18 13:16:10 -0400201
202 if (result != 512) {
srs56942a9f5da2009-08-26 00:48:01 -0400203 printf("\aWARNING! Sector size is not 512 bytes! This program is likely to ");
204 printf("misbehave!\nProceed at your own risk!\n\n");
srs5694e7b4ff92009-08-18 13:16:10 -0400205 } // if
206
207 if (err == -1)
208 result = -1;
209 return (result);
210} // GetBlockSize()
211
srs56942a9f5da2009-08-26 00:48:01 -0400212// Return a plain-text name for a partition type.
srs5694e7b4ff92009-08-18 13:16:10 -0400213// Convert a GUID to a string representation, suitable for display
214// to humans....
215char* GUIDToStr(struct GUIDData theGUID, char* theString) {
216 uint64_t block;
217
218 if (theString != NULL) {
219 block = (theGUID.data1 & UINT64_C(0x00000000FFFFFFFF));
220 sprintf(theString, "%08llX-", (unsigned long long) block);
221 block = (theGUID.data1 & UINT64_C(0x0000FFFF00000000)) >> 32;
222 sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
223 block = (theGUID.data1 & UINT64_C(0xFFFF000000000000)) >> 48;
224 sprintf(theString, "%s%04llX-", theString, (unsigned long long) block);
225 block = (theGUID.data2 & UINT64_C(0x00000000000000FF));
226 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
227 block = (theGUID.data2 & UINT64_C(0x000000000000FF00)) >> 8;
228 sprintf(theString, "%s%02llX-", theString, (unsigned long long) block);
229 block = (theGUID.data2 & UINT64_C(0x0000000000FF0000)) >> 16;
230 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
231 block = (theGUID.data2 & UINT64_C(0x00000000FF000000)) >> 24;
232 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
233 block = (theGUID.data2 & UINT64_C(0x000000FF00000000)) >> 32;
234 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
235 block = (theGUID.data2 & UINT64_C(0x0000FF0000000000)) >> 40;
236 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
237 block = (theGUID.data2 & UINT64_C(0x00FF000000000000)) >> 48;
238 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
239 block = (theGUID.data2 & UINT64_C(0xFF00000000000000)) >> 56;
240 sprintf(theString, "%s%02llX", theString, (unsigned long long) block);
241 } // if
242 return theString;
243} // GUIDToStr()
244
245// Get a GUID from the user
246GUIDData GetGUID(void) {
247 uint64_t part1, part2, part3, part4, part5;
248 int entered = 0;
249 char temp[255], temp2[255];
250 GUIDData theGUID;
251
252 printf("\nA GUID is entered in five segments of from two to six bytes, with\n"
253 "dashes between segments.\n");
254 printf("Enter the entire GUID, a four-byte hexadecimal number for the first segment, or\n"
255 "'R' to generate the entire GUID randomly: ");
256 fgets(temp, 255, stdin);
257
258 // If user entered 'r' or 'R', generate GUID randomly....
259 if ((temp[0] == 'r') || (temp[0] == 'R')) {
260 theGUID.data1 = (uint64_t) rand() * (uint64_t) rand();
261 theGUID.data2 = (uint64_t) rand() * (uint64_t) rand();
262 entered = 1;
263 } // if user entered 'R' or 'r'
264
265 // If string length is right for whole entry, try to parse it....
266 if ((strlen(temp) == 37) && (entered == 0)) {
267 strncpy(temp2, &temp[0], 8);
268 temp2[8] = '\0';
269 sscanf(temp2, "%llx", &part1);
270 strncpy(temp2, &temp[9], 4);
271 temp2[4] = '\0';
272 sscanf(temp2, "%llx", &part2);
273 strncpy(temp2, &temp[14], 4);
274 temp2[4] = '\0';
275 sscanf(temp2, "%llx", &part3);
276 theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
277 strncpy(temp2, &temp[19], 4);
278 temp2[4] = '\0';
279 sscanf(temp2, "%llx", &part4);
280 strncpy(temp2, &temp[24], 12);
281 temp2[12] = '\0';
282 sscanf(temp2, "%llx", &part5);
283 theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
284 ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
285 ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
286 ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
287 ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
288 ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
289 ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
290 ((part5 & UINT64_C(0x00000000000000FF)) << 56);
291 entered = 1;
292 } // if
293
294 // If neither of the above methods of entry was used, use prompted
295 // entry....
296 if (entered == 0) {
297 sscanf(temp, "%llx", &part1);
298 printf("Enter a two-byte hexadecimal number for the second segment: ");
299 fgets(temp, 255, stdin);
300 sscanf(temp, "%llx", &part2);
301 printf("Enter a two-byte hexadecimal number for the third segment: ");
302 fgets(temp, 255, stdin);
303 sscanf(temp, "%llx", &part3);
304 theGUID.data1 = (part3 << 48) + (part2 << 32) + part1;
305 printf("Enter a two-byte hexadecimal number for the fourth segment: ");
306 fgets(temp, 255, stdin);
307 sscanf(temp, "%llx", &part4);
308 printf("Enter a six-byte hexadecimal number for the fifth segment: ");
309 fgets(temp, 255, stdin);
310 sscanf(temp, "%llx", &part5);
311 theGUID.data2 = ((part4 & UINT64_C(0x000000000000FF00)) >> 8) +
312 ((part4 & UINT64_C(0x00000000000000FF)) << 8) +
313 ((part5 & UINT64_C(0x0000FF0000000000)) >> 24) +
314 ((part5 & UINT64_C(0x000000FF00000000)) >> 8) +
315 ((part5 & UINT64_C(0x00000000FF000000)) << 8) +
316 ((part5 & UINT64_C(0x0000000000FF0000)) << 24) +
317 ((part5 & UINT64_C(0x000000000000FF00)) << 40) +
318 ((part5 & UINT64_C(0x00000000000000FF)) << 56);
319 entered = 1;
320 } // if/else
321 printf("New GUID: %s\n", GUIDToStr(theGUID, temp));
322 return theGUID;
323} // GetGUID()
324
srs56942a9f5da2009-08-26 00:48:01 -0400325// Return 1 if the CPU architecture is little endian, 0 if it's big endian....
326int IsLittleEndian(void) {
327 int littleE = 1; // assume little-endian (Intel-style)
328 union {
329 uint32_t num;
330 unsigned char uc[sizeof(uint32_t)];
331 } endian;
332
333 endian.num = 1;
334 if (endian.uc[0] != (unsigned char) 1) {
335 littleE = 0;
336 } // if
337 return (littleE);
338} // IsLittleEndian()
339
340// Reverse the byte order of theValue; numBytes is number of bytes
srs5694221e0872009-08-29 15:00:31 -0400341void ReverseBytes(void* theValue, int numBytes) {
342 char* origValue;
srs56942a9f5da2009-08-26 00:48:01 -0400343 char* tempValue;
344 int i;
345
srs5694221e0872009-08-29 15:00:31 -0400346 origValue = (char*) theValue;
srs56942a9f5da2009-08-26 00:48:01 -0400347 tempValue = (char*) malloc(numBytes);
348 for (i = 0; i < numBytes; i++)
srs5694221e0872009-08-29 15:00:31 -0400349 tempValue[i] = origValue[i];
srs56942a9f5da2009-08-26 00:48:01 -0400350 for (i = 0; i < numBytes; i++)
srs5694221e0872009-08-29 15:00:31 -0400351 origValue[i] = tempValue[numBytes - i - 1];
srs56942a9f5da2009-08-26 00:48:01 -0400352 free(tempValue);
353} // ReverseBytes()
354
srs5694e7b4ff92009-08-18 13:16:10 -0400355// Compute (2 ^ value). Given the return type, value must be 63 or less.
356// Used in some bit-fiddling functions
357uint64_t PowerOf2(int value) {
358 uint64_t retval = 1;
359 int i;
360
361 if ((value < 64) && (value >= 0)) {
362 for (i = 0; i < value; i++) {
363 retval *= 2;
364 } // for
365 } else retval = 0;
366 return retval;
367} // PowerOf2()
368
srs5694e4ac11e2009-08-31 10:13:04 -0400369// An extended file-open function. This includes some system-specific checks.
370// I want them in a function because I use these calls twice and I don't want
371// to forget to change them in one location if I need to change them in
372// the other....
373int OpenForWrite(char* deviceFilename) {
374 int fd;
375
376 fd = open(deviceFilename, O_WRONLY); // try to open the device; may fail....
377#ifdef __APPLE__
378 // MacOS X requires a shared lock under some circumstances....
379 if (fd < 0) {
380 fd = open(deviceFilename, O_WRONLY|O_SHLOCK);
381 } // if
382#endif
383} // MyOpen()
srs5694221e0872009-08-29 15:00:31 -0400384
srs5694e7b4ff92009-08-18 13:16:10 -0400385/**************************************************************************************
386 * *
387 * Below functions are lifted from various sources, as documented in comments before *
388 * each one. *
389 * *
390 **************************************************************************************/
391
392// The disksize function is taken from the Linux fdisk code and modified
393// to work around a problem returning a uint64_t value on Mac OS.
394uint64_t disksize(int fd, int *err) {
395 long sz; // Do not delete; needed for Linux
396 long long b; // Do not delete; needed for Linux
397 uint64_t sectors;
398
399 // Note to self: I recall testing a simplified version of
400 // this code, similar to what's in the __APPLE__ block,
401 // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
402 // systems but not on 64-bit. Keep this in mind in case of
403 // 32/64-bit issues on MacOS....
404#ifdef __APPLE__
405 *err = ioctl(fd, DKIOCGETBLOCKCOUNT, &sectors);
406#else
srs5694221e0872009-08-29 15:00:31 -0400407#ifdef __FreeBSD__
408 *err = ioctl(fd, DIOCGMEDIASIZE, &sz);
409 b = GetBlockSize(fd);
410 sectors = sz / b;
411#else
srs5694e7b4ff92009-08-18 13:16:10 -0400412 *err = ioctl(fd, BLKGETSIZE, &sz);
413 if (*err) {
414 sz = 0;
415 if (errno != EFBIG)
416 return sz;
417 }
418 *err = ioctl(fd, BLKGETSIZE64, &b);
419 if (*err || b == 0 || b == sz)
420 sectors = sz;
421 else
422 sectors = (b >> 9);
423#endif
srs5694221e0872009-08-29 15:00:31 -0400424#endif
srs5694e7b4ff92009-08-18 13:16:10 -0400425 return sectors;
426}