1// 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 6/* This program is copyright (c) 2009-2024 by Roderick W. Smith. It is distributed 7 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ 8 9#define __STDC_LIMIT_MACROS 10#ifndef __STDC_CONSTANT_MACROS 11#define __STDC_CONSTANT_MACROS 12#endif 13#ifndef __STDC_FORMAT_MACROS 14#define __STDC_FORMAT_MACROS 15#endif 16 17#include <inttypes.h> 18#include <stdio.h> 19#include <stdint.h> 20#include <errno.h> 21#include <fcntl.h> 22#include <string.h> 23#include <sys/stat.h> 24#include <string> 25#include <cctype> 26#include <algorithm> 27#include <iostream> 28#include <sstream> 29#include "support.h" 30 31#include <sys/types.h> 32 33// As of 1/2010, BLKPBSZGET is very new, so I'm explicitly defining it if 34// it's not already defined. This should become unnecessary in the future. 35// Note that this is a Linux-only ioctl.... 36#ifndef BLKPBSZGET 37#define BLKPBSZGET _IO(0x12,123) 38#endif 39 40using namespace std; 41 42// Reads a string from stdin, returning it as a C++-style string. 43// Note that the returned string will NOT include the carriage return 44// entered by the user. 45#ifdef EFI 46extern int __sscanf( const char * str , const char * format , ... ) ; 47string ReadString(void) { 48 string inString; 49 char efiString[256]; 50 int stringLength; 51 52 if (fgets(efiString, 255, stdin) != NULL) { 53 stringLength = strlen(efiString); 54 if ((stringLength > 0) && (efiString[stringLength - 1] == '\n')) 55 efiString[stringLength - 1] = '\0'; 56 inString = efiString; 57 } else { 58 inString = ""; 59 } 60 return inString; 61} // ReadString() 62#else 63string ReadString(void) { 64 string inString; 65 66 cout << flush; 67 getline(cin, inString); 68 if (!cin.good()) 69 exit(5); 70 return inString; 71} // ReadString() 72#endif 73 74// Get a numeric value from the user, between low and high (inclusive). 75// Keeps looping until the user enters a value within that range. 76// If user provides no input, def (default value) is returned. 77// (If def is outside of the low-high range, an explicit response 78// is required.) 79uint64_t GetNumber(uint64_t low, uint64_t high, uint64_t def, const string & prompt) { 80 uint64_t response, num; 81 char line[255]; 82 83 if (low != high) { // bother only if low and high differ... 84 do { 85 cout << prompt << flush; 86 cin.getline(line, 255); 87 if (!cin.good()) 88 exit(5); 89 num = sscanf(line, "%" PRIu64, &response); 90 if (num == 1) { // user provided a response 91 if ((response < low) || (response > high)) 92 cout << "Value out of range\n"; 93 } else { // user hit enter; return default 94 response = def; 95 } // if/else 96 } while ((response < low) || (response > high)); 97 } else { // low == high, so return this value 98 cout << "Using " << low << "\n"; 99 response = low; 100 } // else 101 return (response); 102} // GetNumber() 103 104// Gets a Y/N response (and converts lowercase to uppercase) 105char GetYN(void) { 106 char response; 107 string line; 108 bool again = 0 ; 109 110 do { 111 if ( again ) { cout << "Your option? " ; } 112 again = 1 ; 113 cout << "(Y/N): " << flush; 114 line = ReadString(); 115 response = toupper(line[0]); 116 } while ((response != 'Y') && (response != 'N')); 117 return response; 118} // GetYN(void) 119 120// Convert an IEEE-1541-2002 value (K, M, G, T, P, or E) to its equivalent in 121// number of sectors. If no units are appended, interprets as the number 122// of sectors; otherwise, interprets as number of specified units and 123// converts to sectors. For instance, with 512-byte sectors, "1K" converts 124// to 2. If value includes a "+", adds low and subtracts 1; if inValue 125// inclues a "-", subtracts from high. If IeeeValue is empty, returns def. 126// Returns final sector value. In case inValue is invalid, returns 0 (a 127// sector value that's always in use on GPT and therefore invalid); and if 128// inValue works out to something outside the range low-high, returns the 129// computed value; the calling function is responsible for checking the 130// validity of this value. 131// If inValue contains a decimal number (e.g., "9.5G"), quietly truncate it 132// (to "9G" in this example). 133// NOTE: There's a difference in how GCC and VC++ treat oversized values 134// (say, "999999999999999999999") read via the ">>" operator; GCC turns 135// them into the maximum value for the type, whereas VC++ turns them into 136// 0 values. The result is that IeeeToInt() returns UINT64_MAX when 137// compiled with GCC (and so the value is rejected), whereas when VC++ 138// is used, the default value is returned. 139uint64_t IeeeToInt(string inValue, uint64_t sSize, uint64_t low, uint64_t high, uint32_t sectorAlignment, uint64_t def) { 140 uint64_t response = def, bytesPerUnit, mult = 1, divide = 1; 141 size_t foundAt = 0; 142 char suffix = ' ', plusFlag = ' '; 143 string suffixes = "KMGTPE"; 144 int badInput = 0; // flag bad input; once this goes to 1, other values are irrelevant 145 146 if (sSize == 0) { 147 sSize = SECTOR_SIZE; 148 cerr << "Bug: Sector size invalid in IeeeToInt()!\n"; 149 } // if 150 151 // Remove leading spaces, if present 152 while (inValue[0] == ' ') 153 inValue.erase(0, 1); 154 155 // If present, flag and remove leading plus or minus sign 156 if ((inValue[0] == '+') || (inValue[0] == '-')) { 157 plusFlag = inValue[0]; 158 inValue.erase(0, 1); 159 } // if 160 161 // Extract numeric response and, if present, suffix 162 istringstream inString(inValue); 163 if (((inString.peek() < '0') || (inString.peek() > '9')) && (inString.peek() != -1)) 164 badInput = 1; 165 inString >> response >> suffix; 166 suffix = toupper(suffix); 167 foundAt = suffixes.find(suffix); 168 // If suffix is invalid, try to find a valid one. Done because users 169 // sometimes enter decimal numbers; when they do, suffix becomes 170 // '.', and we need to truncate the number and find the real suffix. 171 while (foundAt > (suffixes.length() - 1) && inString.peek() != -1) { 172 inString >> suffix; 173 foundAt = suffixes.find(suffix); 174 suffix = toupper(suffix); 175 } 176 // If no response, or if response == 0, use default (def) 177 if ((inValue.length() == 0) || (response == 0)) { 178 response = def; 179 suffix = ' '; 180 plusFlag = ' '; 181 } // if 182 183 // Find multiplication and division factors for the suffix 184 if (foundAt != string::npos) { 185 bytesPerUnit = UINT64_C(1) << (10 * (foundAt + 1)); 186 mult = bytesPerUnit / sSize; 187 divide = sSize / bytesPerUnit; 188 } // if 189 190 // Adjust response based on multiplier and plus flag, if present 191 if (mult > 1) { 192 if (response > (UINT64_MAX / mult)) 193 badInput = 1; 194 else 195 response *= mult; 196 } else if (divide > 1) { 197 response /= divide; 198 } // if/elseif 199 200 if (plusFlag == '+') { 201 // Recompute response based on low part of range (if default is within 202 // sectorAlignment sectors of high, which should be the case when 203 // prompting for the end of a range) or the defaut value (if default is 204 // further away from the high value, which should be the case for the 205 // first sector of a partition). 206 if ((high - def) < sectorAlignment) { 207 if (response > 0) 208 response--; 209 if (response > (UINT64_MAX - low)) 210 badInput = 1; 211 else 212 response = response + low; 213 } else { 214 if (response > (UINT64_MAX - def)) 215 badInput = 1; 216 else 217 response = response + def; 218 } // if/else 219 } else if (plusFlag == '-') { 220 if (response > high) 221 badInput = 1; 222 else 223 response = high - response; 224 } // if 225 226 if (badInput) 227 response = UINT64_C(0); 228 229 return response; 230} // IeeeToInt() 231 232// Takes a size and converts this to a size in IEEE-1541-2002 units (KiB, MiB, 233// GiB, TiB, PiB, or EiB), returned in C++ string form. The size is either in 234// units of the sector size or, if that parameter is omitted, in bytes. 235// (sectorSize defaults to 1). Note that this function uses peculiar 236// manual computation of decimal value rather than simply setting 237// theValue.precision() because this isn't possible using the available 238// EFI library. 239string BytesToIeee(uint64_t size, uint32_t sectorSize) { 240 uint64_t sizeInIeee; 241 uint64_t previousIeee; 242 float decimalIeee; 243 uint64_t index = 0; 244 string units, prefixes = " KMGTPEZ"; 245 ostringstream theValue; 246 247 sizeInIeee = previousIeee = size * (uint64_t) sectorSize; 248 while ((sizeInIeee > 1024) && (index < (prefixes.length() - 1))) { 249 index++; 250 previousIeee = sizeInIeee; 251 sizeInIeee /= 1024; 252 } // while 253 if (prefixes[index] == ' ') { 254 theValue << sizeInIeee << " bytes"; 255 } else { 256 units = " iB"; 257 units[1] = prefixes[index]; 258 decimalIeee = ((float) previousIeee - 259 ((float) sizeInIeee * 1024.0) + 51.2) / 102.4; 260 if (decimalIeee >= 10.0) { 261 decimalIeee = 0.0; 262 sizeInIeee++; 263 } 264 theValue << sizeInIeee << "." << (uint32_t) decimalIeee << units; 265 } // if/else 266 return theValue.str(); 267} // BytesToIeee() 268 269// Converts two consecutive characters in the input string into a 270// number, interpreting the string as a hexadecimal number, starting 271// at the specified position. 272unsigned char StrToHex(const string & input, unsigned int position) { 273 unsigned char retval = 0x00; 274 unsigned int temp; 275 276 if (input.length() > position) { 277 sscanf(input.substr(position, 2).c_str(), "%x", &temp); 278 retval = (unsigned char) temp; 279 } // if 280 return retval; 281} // StrToHex() 282 283// Returns 1 if input can be interpreted as a hexadecimal number -- 284// all characters must be spaces, digits, or letters A-F (upper- or 285// lower-case), with at least one valid hexadecimal digit; with the 286// exception of the first two characters, which may be "0x"; otherwise 287// returns 0. 288int IsHex(string input) { 289 int isHex = 1, foundHex = 0, i; 290 291 if (input.substr(0, 2) == "0x") 292 input.erase(0, 2); 293 for (i = 0; i < (int) input.length(); i++) { 294 if ((input[i] < '0') || (input[i] > '9')) { 295 if ((input[i] < 'A') || (input[i] > 'F')) { 296 if ((input[i] < 'a') || (input[i] > 'f')) { 297 if ((input[i] != ' ') && (input[i] != '\n')) { 298 isHex = 0; 299 } 300 } else foundHex = 1; 301 } else foundHex = 1; 302 } else foundHex = 1; 303 } // for 304 if (!foundHex) 305 isHex = 0; 306 return isHex; 307} // IsHex() 308 309// Return 1 if the CPU architecture is little endian, 0 if it's big endian.... 310int IsLittleEndian(void) { 311 int littleE = 1; // assume little-endian (Intel-style) 312 union { 313 uint32_t num; 314 unsigned char uc[sizeof(uint32_t)]; 315 } endian; 316 317 endian.num = 1; 318 if (endian.uc[0] != (unsigned char) 1) { 319 littleE = 0; 320 } // if 321 return (littleE); 322} // IsLittleEndian() 323 324// Reverse the byte order of theValue; numBytes is number of bytes 325void ReverseBytes(void* theValue, int numBytes) { 326 char* tempValue = NULL; 327 int i; 328 329 tempValue = new char [numBytes]; 330 if (tempValue != NULL) { 331 memcpy(tempValue, theValue, numBytes); 332 for (i = 0; i < numBytes; i++) 333 ((char*) theValue)[i] = tempValue[numBytes - i - 1]; 334 delete[] tempValue; 335 } else { 336 cerr << "Could not allocate memory in ReverseBytes()! Terminating\n"; 337 exit(1); 338 } // if/else 339} // ReverseBytes() 340 341// On Windows, display a warning and ask whether to continue. If the user elects 342// not to continue, exit immediately. 343void WinWarning(void) { 344 #ifdef _WIN32 345 cout << "\a************************************************************************\n" 346 << "Most versions of Windows cannot boot from a GPT disk except on a UEFI-based\n" 347 << "computer, and most varieties prior to Vista cannot read GPT disks. Therefore,\n" 348 << "you should exit now unless you understand the implications of converting MBR\n" 349 << "to GPT or creating a new GPT disk layout!\n" 350 << "************************************************************************\n\n"; 351 cout << "Are you SURE you want to continue? "; 352 if (GetYN() != 'Y') 353 exit(0); 354 #endif 355} // WinWarning() 356 357// Returns the input string in lower case 358string ToLower(const string& input) { 359 string lower = input; // allocate correct size through copy 360 361 transform(input.begin(), input.end(), lower.begin(), ::tolower); 362 return lower; 363} // ToLower() 364