1// 2// C++ Implementation: gptpart 3// 4// Description: Class to implement a SINGLE GPT partition 5// 6// 7// Author: Rod Smith <rodsmith@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 30using namespace std; 31 32GPTPart::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 41GPTPart::~GPTPart(void) { 42} // destructor 43 44// Return the gdisk-specific two-byte hex code for the partition 45uint16_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"). 51string 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"). 58UnicodeString 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). 65uint64_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 75UnicodeString 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 80string 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 137int 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. 143int 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. 157void 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. 171void 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. 177void 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. 192void 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 264void GPTPart::SetDefaultDescription(void) { 265 SetName(partitionType.TypeName()); 266} // GPTPart::SetDefaultDescription() 267 268GPTPart & 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. 283bool 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. 291void 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). 363void 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 390void 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 400int 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. 409void 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. 425void 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