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