Home | History | Annotate | Download | only in gptfdisk
      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