Home | History | Annotate | Download | only in gptfdisk
      1 /*
      2     MBRPart class, part of GPT fdisk program family.
      3     Copyright (C) 2011  Roderick W. Smith
      4 
      5     This program is free software; you can redistribute it and/or modify
      6     it under the terms of the GNU General Public License as published by
      7     the Free Software Foundation; either version 2 of the License, or
      8     (at your option) any later version.
      9 
     10     This program is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13     GNU General Public License for more details.
     14 
     15     You should have received a copy of the GNU General Public License along
     16     with this program; if not, write to the Free Software Foundation, Inc.,
     17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18 */
     19 
     20 #define __STDC_LIMIT_MACROS
     21 #ifndef __STDC_CONSTANT_MACROS
     22 #define __STDC_CONSTANT_MACROS
     23 #endif
     24 
     25 #include <stddef.h>
     26 #include <stdint.h>
     27 #include <iostream>
     28 #include "support.h"
     29 #include "mbrpart.h"
     30 
     31 using namespace std;
     32 
     33 uint32_t MBRPart::numHeads = MAX_HEADS;
     34 uint32_t MBRPart::numSecspTrack = MAX_SECSPERTRACK;
     35 uint64_t MBRPart::diskSize = 0;
     36 uint32_t MBRPart::blockSize = 512;
     37 int MBRPart::numInstances = 0;
     38 
     39 MBRPart::MBRPart() {
     40    int i;
     41 
     42    status = 0;
     43    for (i = 0; i < 3; i++) {
     44       firstSector[i] = 0;
     45       lastSector[i] = 0;
     46    } // for
     47    partitionType = 0x00;
     48    firstLBA = 0;
     49    lengthLBA = 0;
     50    includeAs = NONE;
     51    canBePrimary = 0;
     52    canBeLogical = 0;
     53    if (numInstances == 0) {
     54       numHeads = MAX_HEADS;
     55       numSecspTrack = MAX_SECSPERTRACK;
     56       diskSize = 0;
     57       blockSize = 512;
     58    } // if
     59    numInstances++;
     60 }
     61 
     62 MBRPart::MBRPart(const MBRPart& orig) {
     63    numInstances++;
     64    operator=(orig);
     65 }
     66 
     67 MBRPart::~MBRPart() {
     68    numInstances--;
     69 }
     70 
     71 MBRPart& MBRPart::operator=(const MBRPart& orig) {
     72    int i;
     73 
     74    status = orig.status;
     75    for (i = 0; i < 3; i++) {
     76       firstSector[i] = orig.firstSector[i];
     77       lastSector[i] = orig.lastSector[i];
     78    } // for
     79    partitionType = orig.partitionType;
     80    firstLBA = orig.firstLBA;
     81    lengthLBA = orig.lengthLBA;
     82    includeAs = orig.includeAs;
     83    canBePrimary = orig.canBePrimary;
     84    canBeLogical = orig.canBeLogical;
     85    return *this;
     86 } // MBRPart::operator=(const MBRPart& orig)
     87 
     88 // Set partition data from packed MBRRecord structure.
     89 MBRPart& MBRPart::operator=(const struct MBRRecord& orig) {
     90    int i;
     91 
     92    status = orig.status;
     93    for (i = 0; i < 3; i++) {
     94       firstSector[i] = orig.firstSector[i];
     95       lastSector[i] = orig.lastSector[i];
     96    } // for
     97    partitionType = orig.partitionType;
     98    firstLBA = orig.firstLBA;
     99    lengthLBA = orig.lengthLBA;
    100    if (lengthLBA > 0)
    101       includeAs = PRIMARY;
    102    else
    103       includeAs = NONE;
    104    return *this;
    105 } // MBRPart::operator=(const struct MBRRecord& orig)
    106 
    107 // Compare the values, and return a bool result.
    108 // Because this is intended for sorting and a lengthLBA value of 0 denotes
    109 // a partition that's not in use and so that should be sorted upwards,
    110 // we return the opposite of the usual arithmetic result when either
    111 // lengthLBA value is 0.
    112 bool MBRPart::operator<(const MBRPart &other) const {
    113    if (lengthLBA && other.lengthLBA)
    114       return (firstLBA < other.firstLBA);
    115    else
    116       return (other.firstLBA < firstLBA);
    117 } // operator<()
    118 
    119 /**************************************************
    120  *                                                *
    121  * Set information on partitions or disks without *
    122  * interacting with the user....                  *
    123  *                                                *
    124  **************************************************/
    125 
    126 void MBRPart::SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs) {
    127    numHeads = heads;
    128    numSecspTrack = sectors;
    129    diskSize = ds;
    130    blockSize = bs;
    131 } // MBRPart::SetGeometry
    132 
    133 // Empty the partition (zero out all values).
    134 void MBRPart::Empty(void) {
    135    status = UINT8_C(0);
    136    firstSector[0] = UINT8_C(0);
    137    firstSector[1] = UINT8_C(0);
    138    firstSector[2] = UINT8_C(0);
    139    partitionType = UINT8_C(0);
    140    lastSector[0] = UINT8_C(0);
    141    lastSector[1] = UINT8_C(0);
    142    lastSector[2] = UINT8_C(0);
    143    firstLBA = UINT32_C(0);
    144    lengthLBA = UINT32_C(0);
    145    includeAs = NONE;
    146 } // MBRPart::Empty()
    147 
    148 // Sets the type code, but silently refuses to change it to an extended type
    149 // code.
    150 // Returns 1 on success, 0 on failure (extended type code)
    151 int MBRPart::SetType(uint8_t typeCode, int isExtended) {
    152    int allOK = 0;
    153 
    154    if ((isExtended == 1) || ((typeCode != 0x05) && (typeCode != 0x0f) && (typeCode != 0x85))) {
    155       partitionType = typeCode;
    156       allOK = 1;
    157    } // if
    158    return allOK;
    159 } // MBRPart::SetType()
    160 
    161 void MBRPart::SetStartLBA(uint64_t start) {
    162    if (start > UINT32_MAX)
    163       cerr << "Partition start out of range! Continuing, but problems now likely!\n";
    164    firstLBA = (uint32_t) start;
    165    RecomputeCHS();
    166 } // MBRPart::SetStartLBA()
    167 
    168 void MBRPart::SetLengthLBA(uint64_t length) {
    169    if (length > UINT32_MAX)
    170       cerr << "Partition length out of range! Continuing, but problems now likely!\n";
    171    lengthLBA = (uint32_t) length;
    172    RecomputeCHS();
    173 } // MBRPart::SetLengthLBA()
    174 
    175 // Set the start point and length of the partition. This function takes LBA
    176 // values, sets them directly, and sets the CHS values based on the LBA
    177 // values and the current geometry settings.
    178 void MBRPart::SetLocation(uint64_t start, uint64_t length) {
    179    int validCHS;
    180 
    181    if ((start > UINT32_MAX) || (length > UINT32_MAX)) {
    182       cerr << "Partition values out of range in MBRPart::SetLocation()!\n"
    183            << "Continuing, but strange problems are now likely!\n";
    184    } // if
    185    firstLBA = (uint32_t) start;
    186    lengthLBA = (uint32_t) length;
    187    validCHS = RecomputeCHS();
    188 
    189    // If this is a complete 0xEE protective MBR partition, max out its
    190    // CHS last sector value, as per the GPT spec. (Set to 0xffffff,
    191    // although the maximum legal MBR value is 0xfeffff, which is
    192    // actually what GNU Parted and Apple's Disk Utility use, in
    193    // violation of the GPT spec.)
    194    if ((partitionType == 0xEE) && (!validCHS) && (firstLBA == 1) &&
    195        ((lengthLBA == diskSize - 1) || (lengthLBA == UINT32_MAX))) {
    196       lastSector[0] = lastSector[1] = lastSector[2] = 0xFF;
    197    } // if
    198 } // MBRPart::SetLocation()
    199 
    200 // Store the MBR data in the packed structure used for disk I/O...
    201 void MBRPart::StoreInStruct(MBRRecord* theStruct) {
    202    int i;
    203 
    204    theStruct->firstLBA = firstLBA;
    205    theStruct->lengthLBA = lengthLBA;
    206    theStruct->partitionType = partitionType;
    207    theStruct->status = status;
    208    for (i = 0; i < 3; i++) {
    209       theStruct->firstSector[i] = firstSector[i];
    210       theStruct->lastSector[i] = lastSector[i];
    211    } // for
    212 } // MBRPart::StoreInStruct()
    213 
    214 /**********************************************
    215 *                                            *
    216 * Get information on partitions or disks.... *
    217 *                                            *
    218 **********************************************/
    219 
    220 // Returns the last LBA value. Note that this can theoretically be a 33-bit
    221 // value, so we return a 64-bit value. If lengthLBA == 0, returns 0, even if
    222 // firstLBA is non-0.
    223 uint64_t MBRPart::GetLastLBA(void) const {
    224    if (lengthLBA > 0)
    225       return (uint64_t) firstLBA + (uint64_t) lengthLBA - UINT64_C(1);
    226    else
    227       return 0;
    228 } // MBRPart::GetLastLBA()
    229 
    230 // Returns 1 if other overlaps with the current partition, 0 if they don't
    231 // overlap
    232 int MBRPart::DoTheyOverlap (const MBRPart& other) {
    233    return lengthLBA && other.lengthLBA &&
    234           (firstLBA <= other.GetLastLBA()) != (GetLastLBA() < other.firstLBA);
    235 } // MBRPart::DoTheyOverlap()
    236 
    237 /*************************************************
    238  *                                               *
    239  * Adjust information on partitions or disks.... *
    240  *                                               *
    241  *************************************************/
    242 
    243 // Recompute the CHS values for the start and end points.
    244 // Returns 1 if both computed values are within the range
    245 // that can be expressed by that CHS, 0 otherwise.
    246 int MBRPart::RecomputeCHS(void) {
    247    int retval = 1;
    248 
    249    if (lengthLBA > 0) {
    250       retval = LBAtoCHS(firstLBA, firstSector);
    251       retval *= LBAtoCHS(firstLBA + lengthLBA - 1, lastSector);
    252    } // if
    253    return retval;
    254 } // MBRPart::RecomputeCHS()
    255 
    256 // Converts 32-bit LBA value to MBR-style CHS value. Returns 1 if conversion
    257 // was within the range that can be expressed by CHS (including 0, for an
    258 // empty partition), 0 if the value is outside that range, and -1 if chs is
    259 // invalid.
    260 int MBRPart::LBAtoCHS(uint32_t lba, uint8_t * chs) {
    261    uint64_t cylinder, head, sector; // all numbered from 0
    262    uint64_t remainder;
    263    int retval = 1;
    264    int done = 0;
    265 
    266    if (chs != NULL) {
    267       // Special case: In case of 0 LBA value, zero out CHS values....
    268       if (lba == 0) {
    269          chs[0] = chs[1] = chs[2] = UINT8_C(0);
    270          done = 1;
    271       } // if
    272       // If LBA value is too large for CHS, max out CHS values....
    273       if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
    274          chs[0] = 254;
    275          chs[1] = chs[2] = 255;
    276          done = 1;
    277          retval = 0;
    278       } // if
    279       // If neither of the above applies, compute CHS values....
    280       if (!done) {
    281          cylinder = lba / (numHeads * numSecspTrack);
    282          remainder = lba - (cylinder * numHeads * numSecspTrack);
    283          head = remainder / numSecspTrack;
    284          remainder -= head * numSecspTrack;
    285          sector = remainder;
    286          if (head < numHeads)
    287             chs[0] = (uint8_t) head;
    288          else
    289             retval = 0;
    290          if (sector < numSecspTrack) {
    291             chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
    292             chs[2] = (uint8_t) (cylinder & UINT32_C(0xFF));
    293          } else {
    294             retval = 0;
    295          } // if/else
    296       } // if value is expressible and non-0
    297    } else { // Invalid (NULL) chs pointer
    298       retval = -1;
    299    } // if CHS pointer valid
    300    return (retval);
    301 } // MBRPart::LBAtoCHS()
    302 
    303 // Reverses the byte order, but only if we're on a big-endian platform.
    304 // Note that most data come in 8-bit structures, so don't need reversing;
    305 // only the LBA data needs to be reversed....
    306 void MBRPart::ReverseByteOrder(void) {
    307    if (IsLittleEndian() == 0) {
    308       ReverseBytes(&firstLBA, 4);
    309       ReverseBytes(&lengthLBA, 4);
    310    } // if
    311 } // MBRPart::ReverseByteOrder()
    312 
    313 /**************************
    314  *                        *
    315  * User I/O functions.... *
    316  *                        *
    317  **************************/
    318 
    319 // Show MBR data. Should update canBeLogical flags before calling.
    320 // If isGpt == 1, omits the "can be logical" and "can be primary" columns.
    321 void MBRPart::ShowData(int isGpt) {
    322    char bootCode = ' ';
    323 
    324    if (status & 0x80) // it's bootable
    325       bootCode = '*';
    326    cout.fill(' ');
    327    cout << bootCode << "  ";
    328    cout.width(13);
    329    cout << firstLBA;
    330    cout.width(13);
    331    cout << GetLastLBA() << "   ";
    332    switch (includeAs) {
    333       case PRIMARY:
    334          cout << "primary";
    335          break;
    336       case LOGICAL:
    337          cout << "logical";
    338          break;
    339       case NONE:
    340          cout << "omitted";
    341          break;
    342       default:
    343          cout << "error  ";
    344          break;
    345    } // switch
    346    cout.width(7);
    347    if (!isGpt) {
    348       if (canBeLogical)
    349          cout << "     Y      ";
    350       else
    351          cout << "            ";
    352       if (canBePrimary)
    353          cout << "  Y      ";
    354       else
    355          cout << "         ";
    356    } // if
    357    cout << "0x";
    358    cout.width(2);
    359    cout.fill('0');
    360    cout << hex << (int) partitionType << dec << "\n";
    361 } // MBRPart::ShowData()
    362