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 29using namespace std; 30 31uint32_t MBRPart::numHeads = MAX_HEADS; 32uint32_t MBRPart::numSecspTrack = MAX_SECSPERTRACK; 33uint64_t MBRPart::diskSize = 0; 34uint32_t MBRPart::blockSize = 512; 35int MBRPart::numInstances = 0; 36 37MBRPart::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 60MBRPart::MBRPart(const MBRPart& orig) { 61 numInstances++; 62 operator=(orig); 63} 64 65MBRPart::~MBRPart() { 66 numInstances--; 67} 68 69MBRPart& 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. 87MBRPart& 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. 110bool 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 124void 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). 132void 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) 149int 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 159void 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 166void 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. 176void 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... 199void 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. 221uint64_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 230int 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. 244int 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. 258int 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.... 304void 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. 319void 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