srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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 |
Aurimas Liutikas | fcad060 | 2016-05-10 19:16:10 -0700 | [diff] [blame] | 21 | #ifndef __STDC_CONSTANT_MACROS |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 22 | #define __STDC_CONSTANT_MACROS |
Aurimas Liutikas | fcad060 | 2016-05-10 19:16:10 -0700 | [diff] [blame] | 23 | #endif |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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 | |
srs5694 | c2f6e0c | 2011-03-16 02:42:33 -0400 | [diff] [blame] | 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 | |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 119 | /************************************************** |
| 120 | * * |
| 121 | * Set information on partitions or disks without * |
| 122 | * interacting with the user.... * |
| 123 | * * |
| 124 | **************************************************/ |
| 125 | |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 126 | void MBRPart::SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs) { |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 127 | numHeads = heads; |
| 128 | numSecspTrack = sectors; |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 129 | diskSize = ds; |
| 130 | blockSize = bs; |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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) { |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 179 | int validCHS; |
| 180 | |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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; |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 187 | validCHS = RecomputeCHS(); |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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.) |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 194 | if ((partitionType == 0xEE) && (!validCHS) && (firstLBA == 1) && |
| 195 | ((lengthLBA == diskSize - 1) || (lengthLBA == UINT32_MAX))) { |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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 | |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 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 | |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 249 | if (lengthLBA > 0) { |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 250 | retval = LBAtoCHS(firstLBA, firstSector); |
| 251 | retval *= LBAtoCHS(firstLBA + lengthLBA - 1, lastSector); |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 252 | } // if |
srs5694 | a17fe69 | 2011-09-10 20:30:20 -0400 | [diff] [blame] | 253 | return retval; |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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 | |
Roderick W. Smith | 1eea9b0 | 2013-07-06 22:52:58 -0400 | [diff] [blame] | 324 | if (status & 0x80) // it's bootable |
srs5694 | 9631223 | 2011-03-12 01:22:42 -0500 | [diff] [blame] | 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"; |
srs5694 | 699941e | 2011-03-21 21:33:57 -0400 | [diff] [blame] | 361 | } // MBRPart::ShowData() |