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