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