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 
MBRPart()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 
MBRPart(const MBRPart & orig)60 MBRPart::MBRPart(const MBRPart& orig) {
61    numInstances++;
62    operator=(orig);
63 }
64 
~MBRPart()65 MBRPart::~MBRPart() {
66    numInstances--;
67 }
68 
operator =(const MBRPart & orig)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.
operator =(const struct MBRRecord & orig)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.
operator <(const MBRPart & other) const110 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 
SetGeometry(uint32_t heads,uint32_t sectors,uint64_t ds,uint32_t bs)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).
Empty(void)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)
SetType(uint8_t typeCode,int isExtended)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 
SetStartLBA(uint64_t start)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 
SetLengthLBA(uint64_t length)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.
SetLocation(uint64_t start,uint64_t length)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...
StoreInStruct(MBRRecord * theStruct)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.
GetLastLBA(void) const221 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
DoTheyOverlap(const MBRPart & other)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.
RecomputeCHS(void)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.
LBAtoCHS(uint32_t lba,uint8_t * chs)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....
ReverseByteOrder(void)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.
ShowData(int isGpt)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