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