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