1 // 2 // C++ Implementation: gptpart 3 // 4 // Description: Class to implement a SINGLE GPT partition 5 // 6 // 7 // Author: Rod Smith <rodsmith (at) rodsbooks.com>, (C) 2009-2013 8 // 9 // Copyright: See COPYING file that comes with this distribution 10 // 11 // 12 // This program is copyright (c) 2009 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 #ifndef __STDC_CONSTANT_MACROS 17 #define __STDC_CONSTANT_MACROS 18 #endif 19 20 #ifdef USE_UTF16 21 #include <unicode/ustdio.h> 22 #else 23 #define UnicodeString string 24 #endif 25 26 #include <string.h> 27 #include <stdio.h> 28 #include <iostream> 29 #include "gptpart.h" 30 #include "attributes.h" 31 32 using namespace std; 33 34 GPTPart::GPTPart(void) { 35 partitionType.Zero(); 36 uniqueGUID.Zero(); 37 firstLBA = 0; 38 lastLBA = 0; 39 attributes = 0; 40 memset(name, 0, NAME_SIZE * sizeof(name[0]) ); 41 } // Default constructor 42 43 GPTPart::~GPTPart(void) { 44 } // destructor 45 46 // Return the gdisk-specific two-byte hex code for the partition 47 uint16_t GPTPart::GetHexType(void) const { 48 return partitionType.GetHexType(); 49 } // GPTPart::GetHexType() 50 51 // Return a plain-text description of the partition type (e.g., "Linux/Windows 52 // data" or "Linux swap"). 53 string GPTPart::GetTypeName(void) { 54 return partitionType.TypeName(); 55 } // GPTPart::GetNameType() 56 57 #ifdef USE_UTF16 58 // Return a Unicode description of the partition type (e.g., "Linux/Windows 59 // data" or "Linux swap"). 60 UnicodeString GPTPart::GetUTypeName(void) { 61 return partitionType.UTypeName(); 62 } // GPTPart::GetNameType() 63 #endif 64 65 // Compute and return the partition's length (or 0 if the end is incorrectly 66 // set before the beginning). 67 uint64_t GPTPart::GetLengthLBA(void) const { 68 uint64_t length = 0; 69 70 if (firstLBA <= lastLBA) 71 length = lastLBA - firstLBA + UINT64_C(1); 72 return length; 73 } // GPTPart::GetLengthLBA() 74 75 #ifdef USE_UTF16 76 // Return partition's name field, converted to a Unicode string 77 UnicodeString GPTPart::GetDescription(void) { 78 return (UChar*) name; 79 } // GPTPart::GetDescription() 80 #else 81 // Return partition's name field, converted to a C++ UTF-8 string 82 string GPTPart::GetDescription(void) { 83 // convert name to utf32 then to utf8 84 string utf8 ; 85 size_t pos = 0 ; 86 while ( ( pos < NAME_SIZE ) && ( name[ pos ] != 0 ) ) { 87 uint16_t cp = name[ pos ++ ] ; 88 if ( ! IsLittleEndian() ) ReverseBytes( & cp , 2 ) ; 89 // first to utf32 90 uint32_t uni ; 91 if ( cp < 0xd800 || cp > 0xdfff ) { 92 uni = cp ; 93 } // if 94 else if ( cp < 0xdc00 ) { 95 // lead surrogate 96 uni = ( (uint32_t)( cp & 0x3ff ) ) << 10 ; 97 if ( pos >= NAME_SIZE ) { 98 // missing trail surrogate, name[] is invalid 99 break ; 100 } // if 101 cp = name[ pos ++ ] ; 102 if ( cp < 0xdc00 || cp > 0xdfff ) { 103 // invalid trail surrogate, name[] is invalid 104 break ; 105 } // if 106 // trail surrogate 107 uni |= cp & 0x3ff ; 108 uni += 0x10000 ; 109 } // if 110 else { 111 // unexpected trail surrogate, name[] is invalid 112 break ; 113 } // if 114 // then to utf8 115 if ( uni < 0x80 ) { 116 utf8 += (char) uni ; 117 } // if 118 else if ( uni < 0x800 ) { 119 utf8 += (char) ( 0xc0 | ( uni >> 6 ) ) ; 120 utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ; 121 } // if 122 else if ( uni < 0x10000 ) { 123 utf8 += (char) ( 0xe0 | ( uni >> 12 ) ) ; 124 utf8 += (char) ( 0x80 | ( ( uni >> 6 ) & 0x3f ) ) ; 125 utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ; 126 } // if 127 else { 128 utf8 += (char) ( 0xf0 | ( uni >> 18 ) ) ; 129 utf8 += (char) ( 0xe0 | ( ( uni >> 12 ) & 0x3f ) ) ; 130 utf8 += (char) ( 0x80 | ( ( uni >> 6 ) & 0x3f ) ) ; 131 utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ; 132 } // if 133 } 134 return utf8 ; 135 } // GPTPart::GetDescription(), UTF-8 version 136 #endif 137 138 // Return 1 if the partition is in use 139 int GPTPart::IsUsed(void) { 140 return (partitionType != GUIDData("0x00")); 141 } // GPTPart::IsUsed() 142 143 // Returns MBR_SIZED_GOOD, MBR_SIZED_IFFY, or MBR_SIZED_BAD; see comments 144 // in header file for details. 145 int GPTPart::IsSizedForMBR(void) { 146 int retval = MBR_SIZED_GOOD; 147 148 if ((firstLBA > UINT32_MAX) || ((lastLBA - firstLBA) > UINT32_MAX) || (firstLBA > lastLBA)) 149 retval = MBR_SIZED_BAD; 150 else if (lastLBA > UINT32_MAX) 151 retval = MBR_SIZED_IFFY; 152 153 return (retval); 154 } // GPTPart::IsSizedForMBR() 155 156 // Set the type code to the specified one. Also changes the partition 157 // name *IF* the current name is the generic one for the current partition 158 // type. 159 void GPTPart::SetType(PartType t) { 160 #ifdef USE_UTF16 161 if (GetDescription() == partitionType.UTypeName()) { 162 #else 163 if (GetDescription() == partitionType.TypeName()) { 164 #endif 165 SetName(t.TypeName()); 166 } // if 167 partitionType = t; 168 } // GPTPart::SetType() 169 170 #ifdef USE_UTF16 171 // Set the name for a partition to theName, using a C++-style string as 172 // input. 173 void GPTPart::SetName(const string & theName) { 174 SetName((UnicodeString) theName.c_str()); 175 } // GPTPart::SetName() 176 177 // Set the name for a partition to theName, using a Unicode string as 178 // input. 179 void GPTPart::SetName(const UnicodeString & theName) { 180 if (theName.isBogus()) { 181 cerr << "Bogus UTF-16 name found in GPTPart::SetName()! Name not changed!\n"; 182 } else { 183 memset(name, 0, NAME_SIZE * sizeof(name[0]) ); 184 theName.extractBetween(0, NAME_SIZE, (UChar*) name); 185 } // if/else 186 } // GPTPart::SetName() 187 188 #else 189 190 // Set the name for a partition to theName. Note that theName is a 191 // standard C++-style ASCII string, although the GUID partition definition 192 // requires a UTF-16LE string. This function creates a simple-minded copy 193 // for this. 194 void GPTPart::SetName(const string & theName) { 195 // convert utf8 to utf32 then to utf16le 196 size_t len = theName.length() ; 197 size_t pos = 0 ; 198 for ( size_t i = 0 ; pos < NAME_SIZE && i < len ; ) { 199 uint32_t uni ; 200 uint8_t cp = theName[ i ++ ] ; 201 int todo ; 202 if ( cp < 0x80 ) { 203 uni = cp ; 204 todo = 0 ; 205 } // if 206 else if ( cp < 0xc0 || cp > 0xf7 ) { 207 // invalid byte, theName is broken 208 break ; 209 } // if 210 else if ( cp < 0xe0 ) { 211 uni = cp & 0x1f ; 212 todo = 1 ; 213 } // if 214 else if ( cp < 0xf0 ) { 215 uni = cp & 0x0f ; 216 todo = 2 ; 217 } // if 218 else { 219 uni = cp & 0x7 ; 220 todo = 3 ; 221 } // if 222 while ( todo > 0 ) { 223 if ( i >= len ) { 224 // missing continuation byte, theName is broken 225 goto break_converter ; 226 } // if 227 cp = theName[ i ++ ] ; 228 if ( cp > 0xbf || cp < 0x80 ) { 229 // invalid continuation byte, theName is broken 230 goto break_converter ; 231 } // if 232 uni <<= 6 ; 233 uni |= cp & 0x3f ; 234 todo -- ; 235 } // while 236 // then to utf16le 237 if ( uni < 0x10000 ) { 238 name[ pos ] = (uint16_t) uni ; 239 if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ; 240 pos ++ ; 241 } // if 242 else { 243 if ( pos > NAME_SIZE - 2 ) { 244 // not enough room for two surrogates, truncate 245 break ; 246 } // if 247 uni -= 0x10000 ; 248 name[ pos ] = (uint16_t)( uni >> 10 ) | 0xd800 ; 249 if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ; 250 pos ++ ; 251 name[ pos ] = (uint16_t)( uni & 0x3ff ) | 0xdc00 ; 252 if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ; 253 pos ++ ; 254 } 255 } // for 256 break_converter : ; 257 // finally fill with zeroes 258 while ( pos < NAME_SIZE ) { 259 name[ pos ++ ] = 0 ; 260 } // while 261 } // GPTPart::SetName(), UTF-8 version 262 #endif 263 264 // Set the name for the partition based on the current GUID partition type 265 // code's associated name 266 void GPTPart::SetDefaultDescription(void) { 267 SetName(partitionType.TypeName()); 268 } // GPTPart::SetDefaultDescription() 269 270 GPTPart & GPTPart::operator=(const GPTPart & orig) { 271 partitionType = orig.partitionType; 272 uniqueGUID = orig.uniqueGUID; 273 firstLBA = orig.firstLBA; 274 lastLBA = orig.lastLBA; 275 attributes = orig.attributes; 276 memcpy(name, orig.name, NAME_SIZE * sizeof( name[ 0 ] ) ); 277 return *this; 278 } // assignment operator 279 280 // Compare the values, and return a bool result. 281 // Because this is intended for sorting and a firstLBA value of 0 denotes 282 // a partition that's not in use and so that should be sorted upwards, 283 // we return the opposite of the usual arithmetic result when either 284 // firstLBA value is 0. 285 bool GPTPart::operator<(const GPTPart &other) const { 286 if (firstLBA && other.firstLBA) 287 return (firstLBA < other.firstLBA); 288 else 289 return (other.firstLBA < firstLBA); 290 } // GPTPart::operator<() 291 292 // Display summary information; does nothing if the partition is empty. 293 void GPTPart::ShowSummary(int partNum, uint32_t blockSize) { 294 string sizeInIeee; 295 UnicodeString description; 296 size_t i; 297 298 if (firstLBA != 0) { 299 sizeInIeee = BytesToIeee(lastLBA - firstLBA + 1, blockSize); 300 cout.fill(' '); 301 cout.width(4); 302 cout << partNum + 1 << " "; 303 cout.width(14); 304 cout << firstLBA << " "; 305 cout.width(14); 306 cout << lastLBA << " "; 307 cout << sizeInIeee << " "; 308 if (sizeInIeee.length() < 10) 309 for (i = 0; i < 10 - sizeInIeee.length(); i++) 310 cout << " "; 311 cout.fill('0'); 312 cout.width(4); 313 cout.setf(ios::uppercase); 314 cout << hex << partitionType.GetHexType() << " " << dec; 315 cout.fill(' '); 316 #ifdef USE_UTF16 317 GetDescription().extractBetween(0, 23, description); 318 cout << description << "\n"; 319 #else 320 string desc = GetDescription() ; 321 size_t n = 0 ; 322 size_t i = 0 ; 323 size_t len = desc.length() ; 324 while ( n < 22 && i < len ) { 325 i ++ ; 326 if ( i >= len ) { 327 // short description 328 break ; 329 } // if 330 // skip continuation bytes 331 while ( i < len && ( ( desc[ i ] & 0xC0 ) == 0x80 ) ) { 332 // utf8 continuation byte 333 i ++ ; 334 } // while 335 n ++ ; 336 } // while 337 if ( i < len ) { 338 n = 0 ; 339 i = 0 ; 340 // description is long we will truncate it 341 while ( n < 19 && i < len ) { 342 i ++ ; 343 if ( i >= len ) { 344 // should not happen 345 break ; 346 } // if 347 // skip continuation bytes 348 while ( i < len && ( ( desc[ i ] & 0xC0 ) == 0x80 ) ) { 349 // utf8 continuation byte 350 i ++ ; 351 } // while 352 n ++ ; 353 } // while 354 } // for 355 cout << GetDescription().substr( 0 , i ) ; 356 if ( i < len ) cout << "..." ; 357 cout << "\n"; 358 #endif 359 cout.fill(' '); 360 } // if 361 } // GPTPart::ShowSummary() 362 363 // Show detailed partition information. Does nothing if the partition is 364 // empty (as determined by firstLBA being 0). 365 void GPTPart::ShowDetails(uint32_t blockSize) { 366 uint64_t size; 367 368 if (firstLBA != 0) { 369 cout << "Partition GUID code: " << partitionType; 370 cout << " (" << partitionType.TypeName() << ")\n"; 371 cout << "Partition unique GUID: " << uniqueGUID << "\n"; 372 373 cout << "First sector: " << firstLBA << " (at " 374 << BytesToIeee(firstLBA, blockSize) << ")\n"; 375 cout << "Last sector: " << lastLBA << " (at " 376 << BytesToIeee(lastLBA, blockSize) << ")\n"; 377 size = (lastLBA - firstLBA + 1); 378 cout << "Partition size: " << size << " sectors (" 379 << BytesToIeee(size, blockSize) << ")\n"; 380 cout << "Attribute flags: "; 381 cout.fill('0'); 382 cout.width(16); 383 cout << hex; 384 cout << attributes << "\n"; 385 cout << dec; 386 cout << "Partition name: '" << GetDescription() << "'\n"; 387 cout.fill(' '); 388 } // if 389 } // GPTPart::ShowDetails() 390 391 // Blank (delete) a single partition 392 void GPTPart::BlankPartition(void) { 393 uniqueGUID.Zero(); 394 partitionType.Zero(); 395 firstLBA = 0; 396 lastLBA = 0; 397 attributes = 0; 398 memset(name, 0, NAME_SIZE * sizeof( name[0]) ); 399 } // GPTPart::BlankPartition 400 401 // Returns 1 if the two partitions overlap, 0 if they don't 402 int GPTPart::DoTheyOverlap(const GPTPart & other) { 403 // Don't bother checking unless these are defined (both start and end points 404 // are 0 for undefined partitions, so just check the start points) 405 return firstLBA && other.firstLBA && 406 (firstLBA <= other.lastLBA) != (lastLBA < other.firstLBA); 407 } // GPTPart::DoTheyOverlap() 408 409 // Reverse the bytes of integral data types and of the UTF-16LE name; 410 // used on big-endian systems. 411 void GPTPart::ReversePartBytes(void) { 412 int i; 413 414 ReverseBytes(&firstLBA, 8); 415 ReverseBytes(&lastLBA, 8); 416 ReverseBytes(&attributes, 8); 417 for (i = 0; i < NAME_SIZE; i ++ ) 418 ReverseBytes(name + i, 2); 419 } // GPTPart::ReverseBytes() 420 421 /**************************************** 422 * Functions requiring user interaction * 423 ****************************************/ 424 425 // Change the type code on the partition. Also changes the name if the original 426 // name is the generic one for the partition type. 427 void GPTPart::ChangeType(void) { 428 string line; 429 int changeName; 430 PartType tempType = (GUIDData) "00000000-0000-0000-0000-000000000000"; 431 432 #ifdef USE_UTF16 433 changeName = (GetDescription() == GetUTypeName()); 434 #else 435 changeName = (GetDescription() == GetTypeName()); 436 #endif 437 438 cout << "Current type is '" << GetTypeName() << "'\n"; 439 do { 440 cout << "Hex code or GUID (L to show codes, Enter = " << hex << DEFAULT_GPT_TYPE << dec << "): "; 441 line = ReadString(); 442 if ((line[0] == 'L') || (line[0] == 'l')) { 443 partitionType.ShowAllTypes(); 444 } else { 445 if (line.length() == 0) 446 tempType = DEFAULT_GPT_TYPE; 447 else 448 tempType = line; 449 } // if/else 450 } while (tempType == (GUIDData) "00000000-0000-0000-0000-000000000000"); 451 partitionType = tempType; 452 cout << "Changed type of partition to '" << partitionType.TypeName() << "'\n"; 453 if (changeName) { 454 SetDefaultDescription(); 455 } // if 456 } // GPTPart::ChangeType() 457