blob: 81e42ca3a9e5f740147f1e46cc32f95946c34119 [file] [log] [blame]
srs5694f2efa7d2011-03-01 22:03:26 -05001/* basicmbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
2 data. */
3
4/* Initial coding by Rod Smith, January to February, 2009 */
5
Roderick W. Smithe3ee7332013-09-24 12:56:11 -04006/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
srs5694f2efa7d2011-03-01 22:03:26 -05007 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
8
9#define __STDC_LIMIT_MACROS
Aurimas Liutikasfcad0602016-05-10 19:16:10 -070010#ifndef __STDC_CONSTANT_MACROS
srs5694f2efa7d2011-03-01 22:03:26 -050011#define __STDC_CONSTANT_MACROS
Aurimas Liutikasfcad0602016-05-10 19:16:10 -070012#endif
srs5694f2efa7d2011-03-01 22:03:26 -050013
14#include <stdio.h>
15#include <stdlib.h>
16#include <stdint.h>
17#include <fcntl.h>
18#include <string.h>
19#include <time.h>
20#include <sys/stat.h>
21#include <errno.h>
22#include <iostream>
srs5694c2f6e0c2011-03-16 02:42:33 -040023#include <algorithm>
srs5694f2efa7d2011-03-01 22:03:26 -050024#include "mbr.h"
srs5694f2efa7d2011-03-01 22:03:26 -050025#include "support.h"
26
27using namespace std;
28
29/****************************************
30 * *
31 * MBRData class and related structures *
32 * *
33 ****************************************/
34
35BasicMBRData::BasicMBRData(void) {
36 blockSize = SECTOR_SIZE;
37 diskSize = 0;
38 device = "";
39 state = invalid;
srs5694f2efa7d2011-03-01 22:03:26 -050040 numHeads = MAX_HEADS;
41 numSecspTrack = MAX_SECSPERTRACK;
42 myDisk = NULL;
43 canDeleteMyDisk = 0;
srs569423d8d542011-10-01 18:40:10 -040044// memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t));
srs5694f2efa7d2011-03-01 22:03:26 -050045 EmptyMBR();
46} // BasicMBRData default constructor
47
48BasicMBRData::BasicMBRData(string filename) {
49 blockSize = SECTOR_SIZE;
50 diskSize = 0;
51 device = filename;
52 state = invalid;
53 numHeads = MAX_HEADS;
54 numSecspTrack = MAX_SECSPERTRACK;
55 myDisk = NULL;
56 canDeleteMyDisk = 0;
srs569423d8d542011-10-01 18:40:10 -040057// memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t));
58
srs5694f2efa7d2011-03-01 22:03:26 -050059 // Try to read the specified partition table, but if it fails....
60 if (!ReadMBRData(filename)) {
61 EmptyMBR();
62 device = "";
63 } // if
64} // BasicMBRData(string filename) constructor
65
66// Free space used by myDisk only if that's OK -- sometimes it will be
67// copied from an outside source, in which case that source should handle
68// it!
69BasicMBRData::~BasicMBRData(void) {
70 if (canDeleteMyDisk)
71 delete myDisk;
72} // BasicMBRData destructor
73
74// Assignment operator -- copy entire set of MBR data.
75BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) {
76 int i;
77
srs5694bf8950c2011-03-12 01:23:12 -050078 memcpy(code, orig.code, 440);
srs5694f2efa7d2011-03-01 22:03:26 -050079 diskSignature = orig.diskSignature;
80 nulls = orig.nulls;
81 MBRSignature = orig.MBRSignature;
82 blockSize = orig.blockSize;
83 diskSize = orig.diskSize;
84 numHeads = orig.numHeads;
85 numSecspTrack = orig.numSecspTrack;
86 canDeleteMyDisk = orig.canDeleteMyDisk;
87 device = orig.device;
88 state = orig.state;
89
90 myDisk = new DiskIO;
srs56946aae2a92011-06-10 01:16:51 -040091 if (myDisk == NULL) {
92 cerr << "Unable to allocate memory in BasicMBRData::operator=()! Terminating!\n";
93 exit(1);
94 } // if
srs5694bf8950c2011-03-12 01:23:12 -050095 if (orig.myDisk != NULL)
96 myDisk->OpenForRead(orig.myDisk->GetName());
srs5694f2efa7d2011-03-01 22:03:26 -050097
98 for (i = 0; i < MAX_MBR_PARTS; i++) {
99 partitions[i] = orig.partitions[i];
100 } // for
101 return *this;
102} // BasicMBRData::operator=()
103
104/**********************
105 * *
106 * Disk I/O functions *
107 * *
108 **********************/
109
110// Read data from MBR. Returns 1 if read was successful (even if the
111// data isn't a valid MBR), 0 if the read failed.
112int BasicMBRData::ReadMBRData(const string & deviceFilename) {
113 int allOK = 1;
114
115 if (myDisk == NULL) {
116 myDisk = new DiskIO;
srs56946aae2a92011-06-10 01:16:51 -0400117 if (myDisk == NULL) {
118 cerr << "Unable to allocate memory in BasicMBRData::ReadMBRData()! Terminating!\n";
119 exit(1);
120 } // if
srs5694f2efa7d2011-03-01 22:03:26 -0500121 canDeleteMyDisk = 1;
122 } // if
123 if (myDisk->OpenForRead(deviceFilename)) {
124 allOK = ReadMBRData(myDisk);
125 } else {
126 allOK = 0;
127 } // if
128
129 if (allOK)
130 device = deviceFilename;
131
132 return allOK;
133} // BasicMBRData::ReadMBRData(const string & deviceFilename)
134
135// Read data from MBR. If checkBlockSize == 1 (the default), the block
136// size is checked; otherwise it's set to the default (512 bytes).
srs569400b6d7a2011-06-26 22:40:06 -0400137// Note that any extended partition(s) present will be omitted from
138// in the partitions[] array; these partitions must be re-created when
139// the partition table is saved in MBR format.
srs5694f2efa7d2011-03-01 22:03:26 -0500140int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
srs569423d8d542011-10-01 18:40:10 -0400141 int allOK = 1, i, logicalNum = 3;
srs5694f2efa7d2011-03-01 22:03:26 -0500142 int err = 1;
143 TempMBR tempMBR;
144
srs5694bf8950c2011-03-12 01:23:12 -0500145 if ((myDisk != NULL) && (myDisk != theDisk) && (canDeleteMyDisk)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500146 delete myDisk;
147 canDeleteMyDisk = 0;
148 } // if
149
150 myDisk = theDisk;
151
152 // Empty existing MBR data, including the logical partitions...
153 EmptyMBR(0);
154
155 if (myDisk->Seek(0))
156 if (myDisk->Read(&tempMBR, 512))
157 err = 0;
158 if (err) {
159 cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n";
160 } else {
161 for (i = 0; i < 440; i++)
162 code[i] = tempMBR.code[i];
163 diskSignature = tempMBR.diskSignature;
164 nulls = tempMBR.nulls;
165 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500166 partitions[i] = tempMBR.partitions[i];
167 if (partitions[i].GetLengthLBA() > 0)
168 partitions[i].SetInclusion(PRIMARY);
srs5694f2efa7d2011-03-01 22:03:26 -0500169 } // for i... (reading all four partitions)
170 MBRSignature = tempMBR.MBRSignature;
srs5694bf8950c2011-03-12 01:23:12 -0500171 ReadCHSGeom();
srs5694f2efa7d2011-03-01 22:03:26 -0500172
173 // Reverse the byte order, if necessary
174 if (IsLittleEndian() == 0) {
175 ReverseBytes(&diskSignature, 4);
176 ReverseBytes(&nulls, 2);
177 ReverseBytes(&MBRSignature, 2);
178 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500179 partitions[i].ReverseByteOrder();
srs5694f2efa7d2011-03-01 22:03:26 -0500180 } // for
181 } // if
182
183 if (MBRSignature != MBR_SIGNATURE) {
184 allOK = 0;
185 state = invalid;
186 } // if
187
188 // Find disk size
189 diskSize = myDisk->DiskSize(&err);
190
191 // Find block size
192 if (checkBlockSize) {
193 blockSize = myDisk->GetBlockSize();
194 } // if (checkBlockSize)
195
196 // Load logical partition data, if any is found....
197 if (allOK) {
198 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500199 if ((partitions[i].GetType() == 0x05) || (partitions[i].GetType() == 0x0f)
200 || (partitions[i].GetType() == 0x85)) {
srs569423d8d542011-10-01 18:40:10 -0400201 // Found it, so call a function to load everything from them....
202 logicalNum = ReadLogicalParts(partitions[i].GetStartLBA(), abs(logicalNum) + 1);
203 if (logicalNum < 0) {
srs5694f2efa7d2011-03-01 22:03:26 -0500204 cerr << "Error reading logical partitions! List may be truncated!\n";
205 } // if maxLogicals valid
srs5694bf8950c2011-03-12 01:23:12 -0500206 DeletePartition(i);
srs5694f2efa7d2011-03-01 22:03:26 -0500207 } // if primary partition is extended
208 } // for primary partition loop
209 if (allOK) { // Loaded logicals OK
210 state = mbr;
211 } else {
212 state = invalid;
213 } // if
214 } // if
215
216 // Check to see if it's in GPT format....
217 if (allOK) {
218 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500219 if (partitions[i].GetType() == UINT8_C(0xEE)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500220 state = gpt;
221 } // if
222 } // for
223 } // if
224
225 // If there's an EFI GPT partition, look for other partition types,
226 // to flag as hybrid
227 if (state == gpt) {
228 for (i = 0 ; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500229 if ((partitions[i].GetType() != UINT8_C(0xEE)) &&
230 (partitions[i].GetType() != UINT8_C(0x00)))
srs5694f2efa7d2011-03-01 22:03:26 -0500231 state = hybrid;
srs569423d8d542011-10-01 18:40:10 -0400232 if (logicalNum != 3)
srs5694bf8950c2011-03-12 01:23:12 -0500233 cerr << "Warning! MBR Logical partitions found on a hybrid MBR disk! This is an\n"
234 << "EXTREMELY dangerous configuration!\n\a";
srs5694f2efa7d2011-03-01 22:03:26 -0500235 } // for
236 } // if (hybrid detection code)
237 } // no initial error
238 return allOK;
239} // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
240
srs569423d8d542011-10-01 18:40:10 -0400241// This is a function to read all the logical partitions, following the
srs5694f2efa7d2011-03-01 22:03:26 -0500242// logical partition linked list from the disk and storing the basic data in the
srs569423d8d542011-10-01 18:40:10 -0400243// partitions[] array. Returns last index to partitions[] used, or -1 times the
244// that index if there was a problem. (Some problems can leave valid logical
245// partition data.)
srs5694f2efa7d2011-03-01 22:03:26 -0500246// Parameters:
247// extendedStart = LBA of the start of the extended partition
srs569423d8d542011-10-01 18:40:10 -0400248// partNum = number of first partition in extended partition (normally 4).
249int BasicMBRData::ReadLogicalParts(uint64_t extendedStart, int partNum) {
srs5694f2efa7d2011-03-01 22:03:26 -0500250 struct TempMBR ebr;
srs569423d8d542011-10-01 18:40:10 -0400251 int i, another = 1, allOK = 1;
srs5694bf8950c2011-03-12 01:23:12 -0500252 uint8_t ebrType;
srs5694f2efa7d2011-03-01 22:03:26 -0500253 uint64_t offset;
srs569423d8d542011-10-01 18:40:10 -0400254 uint64_t EbrLocations[MAX_MBR_PARTS];
srs5694f2efa7d2011-03-01 22:03:26 -0500255
srs569423d8d542011-10-01 18:40:10 -0400256 offset = extendedStart;
257 memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint64_t));
258 while (another && (partNum < MAX_MBR_PARTS) && (partNum >= 0) && (allOK > 0)) {
259 for (i = 0; i < MAX_MBR_PARTS; i++) {
260 if (EbrLocations[i] == offset) { // already read this one; infinite logical partition loop!
261 cerr << "Logical partition infinite loop detected! This is being corrected.\n";
262 allOK = -1;
Jasraj Bedi7e160fb2020-06-06 01:42:05 +0000263 if(partNum > 0) //don't go negative
264 partNum -= 1;
srs569423d8d542011-10-01 18:40:10 -0400265 } // if
266 } // for
267 EbrLocations[partNum] = offset;
srs5694f2efa7d2011-03-01 22:03:26 -0500268 if (myDisk->Seek(offset) == 0) { // seek to EBR record
269 cerr << "Unable to seek to " << offset << "! Aborting!\n";
srs569423d8d542011-10-01 18:40:10 -0400270 allOK = -1;
srs5694f2efa7d2011-03-01 22:03:26 -0500271 }
272 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
273 cerr << "Error seeking to or reading logical partition data from " << offset
srs569423d8d542011-10-01 18:40:10 -0400274 << "!\nSome logical partitions may be missing!\n";
275 allOK = -1;
srs5694f2efa7d2011-03-01 22:03:26 -0500276 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
277 ReverseBytes(&ebr.MBRSignature, 2);
278 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
279 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
280 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
281 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
282 } // if/else/if
283
284 if (ebr.MBRSignature != MBR_SIGNATURE) {
srs569423d8d542011-10-01 18:40:10 -0400285 allOK = -1;
286 cerr << "EBR signature for logical partition invalid; read 0x";
srs5694f2efa7d2011-03-01 22:03:26 -0500287 cerr.fill('0');
288 cerr.width(4);
289 cerr.setf(ios::uppercase);
290 cerr << hex << ebr.MBRSignature << ", but should be 0x";
291 cerr.width(4);
292 cerr << MBR_SIGNATURE << dec << "\n";
293 cerr.fill(' ');
294 } // if
295
srs569423d8d542011-10-01 18:40:10 -0400296 if ((partNum >= 0) && (partNum < MAX_MBR_PARTS) && (allOK > 0)) {
297 // Sometimes an EBR points directly to another EBR, rather than defining
298 // a logical partition and then pointing to another EBR. Thus, we skip
299 // the logical partition when this is the case....
300 ebrType = ebr.partitions[0].partitionType;
301 if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) {
Aurimas Liutikasbdbab022017-03-07 09:50:36 -0800302 cout << "EBR describes a logical partition!\n";
srs569423d8d542011-10-01 18:40:10 -0400303 offset = extendedStart + ebr.partitions[0].firstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500304 } else {
srs569423d8d542011-10-01 18:40:10 -0400305 // Copy over the basic data....
306 partitions[partNum] = ebr.partitions[0];
307 // Adjust the start LBA, since it's encoded strangely....
308 partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + offset);
309 partitions[partNum].SetInclusion(LOGICAL);
310
311 // Find the next partition (if there is one)
312 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum < (MAX_MBR_PARTS - 1))) {
313 offset = extendedStart + ebr.partitions[1].firstLBA;
314 partNum++;
315 } else {
316 another = 0;
317 } // if another partition
318 } // if/else
319 } // if
320 } // while()
321 return (partNum * allOK);
srs5694f2efa7d2011-03-01 22:03:26 -0500322} // BasicMBRData::ReadLogicalPart()
323
324// Write the MBR data to the default defined device. This writes both the
325// MBR itself and any defined logical partitions, provided there's an
326// MBR extended partition.
327int BasicMBRData::WriteMBRData(void) {
328 int allOK = 1;
329
330 if (myDisk != NULL) {
331 if (myDisk->OpenForWrite() != 0) {
332 allOK = WriteMBRData(myDisk);
srs5694bf8950c2011-03-12 01:23:12 -0500333 cout << "Done writing data!\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500334 } else {
335 allOK = 0;
336 } // if/else
337 myDisk->Close();
338 } else allOK = 0;
339 return allOK;
340} // BasicMBRData::WriteMBRData(void)
341
342// Save the MBR data to a file. This writes both the
srs5694bf8950c2011-03-12 01:23:12 -0500343// MBR itself and any defined logical partitions.
srs5694f2efa7d2011-03-01 22:03:26 -0500344int BasicMBRData::WriteMBRData(DiskIO *theDisk) {
srs5694bf8950c2011-03-12 01:23:12 -0500345 int i, j, partNum, next, allOK = 1, moreLogicals = 0;
346 uint64_t extFirstLBA = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500347 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
348 TempMBR tempMBR;
349
srs5694bf8950c2011-03-12 01:23:12 -0500350 allOK = CreateExtended();
351 if (allOK) {
352 // First write the main MBR data structure....
353 memcpy(tempMBR.code, code, 440);
354 tempMBR.diskSignature = diskSignature;
355 tempMBR.nulls = nulls;
356 tempMBR.MBRSignature = MBRSignature;
357 for (i = 0; i < 4; i++) {
358 partitions[i].StoreInStruct(&tempMBR.partitions[i]);
359 if (partitions[i].GetType() == 0x0f) {
360 extFirstLBA = partitions[i].GetStartLBA();
361 moreLogicals = 1;
362 } // if
363 } // for i...
364 } // if
365 allOK = allOK && WriteMBRData(tempMBR, theDisk, 0);
srs5694f2efa7d2011-03-01 22:03:26 -0500366
367 // Set up tempMBR with some constant data for logical partitions...
368 tempMBR.diskSignature = 0;
369 for (i = 2; i < 4; i++) {
370 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
371 tempMBR.partitions[i].partitionType = 0x00;
372 for (j = 0; j < 3; j++) {
373 tempMBR.partitions[i].firstSector[j] = 0;
374 tempMBR.partitions[i].lastSector[j] = 0;
375 } // for j
376 } // for i
377
srs5694bf8950c2011-03-12 01:23:12 -0500378 partNum = FindNextInUse(4);
srs5694f2efa7d2011-03-01 22:03:26 -0500379 writeEbrTo = (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500380 // Write logicals...
srs569423d8d542011-10-01 18:40:10 -0400381 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
srs5694bf8950c2011-03-12 01:23:12 -0500382 partitions[partNum].StoreInStruct(&tempMBR.partitions[0]);
383 tempMBR.partitions[0].firstLBA = 1;
srs5694f2efa7d2011-03-01 22:03:26 -0500384 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
srs5694bf8950c2011-03-12 01:23:12 -0500385 next = FindNextInUse(partNum + 1);
386 if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500387 tempMBR.partitions[1].partitionType = 0x0f;
srs5694bf8950c2011-03-12 01:23:12 -0500388 tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1);
389 tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 1);
srs5694f2efa7d2011-03-01 22:03:26 -0500390 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
391 (uint8_t *) &tempMBR.partitions[1].firstSector);
392 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
393 (uint8_t *) &tempMBR.partitions[1].lastSector);
394 } else {
395 tempMBR.partitions[1].partitionType = 0x00;
396 tempMBR.partitions[1].firstLBA = 0;
397 tempMBR.partitions[1].lengthLBA = 0;
398 moreLogicals = 0;
399 } // if/else
400 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
srs5694f2efa7d2011-03-01 22:03:26 -0500401 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500402 partNum = next;
srs5694f2efa7d2011-03-01 22:03:26 -0500403 } // while
srs5694bf8950c2011-03-12 01:23:12 -0500404 DeleteExtendedParts();
srs5694f2efa7d2011-03-01 22:03:26 -0500405 return allOK;
406} // BasicMBRData::WriteMBRData(DiskIO *theDisk)
407
408int BasicMBRData::WriteMBRData(const string & deviceFilename) {
409 device = deviceFilename;
410 return WriteMBRData();
411} // BasicMBRData::WriteMBRData(const string & deviceFilename)
412
413// Write a single MBR record to the specified sector. Used by the like-named
414// function to write both the MBR and multiple EBR (for logical partition)
415// records.
416// Returns 1 on success, 0 on failure
417int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
418 int i, allOK;
419
420 // Reverse the byte order, if necessary
421 if (IsLittleEndian() == 0) {
422 ReverseBytes(&mbr.diskSignature, 4);
423 ReverseBytes(&mbr.nulls, 2);
424 ReverseBytes(&mbr.MBRSignature, 2);
425 for (i = 0; i < 4; i++) {
426 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
427 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
428 } // for
429 } // if
430
431 // Now write the data structure...
432 allOK = theDisk->OpenForWrite();
433 if (allOK && theDisk->Seek(sector)) {
434 if (theDisk->Write(&mbr, 512) != 512) {
435 allOK = 0;
436 cerr << "Error " << errno << " when saving MBR!\n";
437 } // if
438 } else {
439 allOK = 0;
440 cerr << "Error " << errno << " when seeking to MBR to write it!\n";
441 } // if/else
442 theDisk->Close();
443
444 // Reverse the byte order back, if necessary
445 if (IsLittleEndian() == 0) {
446 ReverseBytes(&mbr.diskSignature, 4);
447 ReverseBytes(&mbr.nulls, 2);
448 ReverseBytes(&mbr.MBRSignature, 2);
449 for (i = 0; i < 4; i++) {
450 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
451 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
452 } // for
453 }// if
454 return allOK;
455} // BasicMBRData::WriteMBRData(uint64_t sector)
456
srs5694bf8950c2011-03-12 01:23:12 -0500457// Set a new disk device; used in copying one disk's partition
458// table to another disk.
459void BasicMBRData::SetDisk(DiskIO *theDisk) {
460 int err;
461
462 myDisk = theDisk;
463 diskSize = theDisk->DiskSize(&err);
464 canDeleteMyDisk = 0;
465 ReadCHSGeom();
466} // BasicMBRData::SetDisk()
467
srs5694f2efa7d2011-03-01 22:03:26 -0500468/********************************************
469 * *
470 * Functions that display data for the user *
471 * *
472 ********************************************/
473
474// Show the MBR data to the user, up to the specified maximum number
475// of partitions....
srs5694bf8950c2011-03-12 01:23:12 -0500476void BasicMBRData::DisplayMBRData(void) {
srs5694f2efa7d2011-03-01 22:03:26 -0500477 int i;
srs5694f2efa7d2011-03-01 22:03:26 -0500478
srs5694bf8950c2011-03-12 01:23:12 -0500479 cout << "\nDisk size is " << diskSize << " sectors ("
srs569401f7f082011-03-15 23:53:31 -0400480 << BytesToIeee(diskSize, blockSize) << ")\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500481 cout << "MBR disk identifier: 0x";
482 cout.width(8);
483 cout.fill('0');
484 cout.setf(ios::uppercase);
485 cout << hex << diskSignature << dec << "\n";
srs5694bf8950c2011-03-12 01:23:12 -0500486 cout << "MBR partitions:\n\n";
487 if ((state == gpt) || (state == hybrid)) {
488 cout << "Number Boot Start Sector End Sector Status Code\n";
489 } else {
490 cout << " Can Be Can Be\n";
491 cout << "Number Boot Start Sector End Sector Status Logical Primary Code\n";
492 UpdateCanBeLogical();
493 } //
494 for (i = 0; i < MAX_MBR_PARTS; i++) {
495 if (partitions[i].GetLengthLBA() != 0) {
srs5694f2efa7d2011-03-01 22:03:26 -0500496 cout.fill(' ');
497 cout.width(4);
srs5694bf8950c2011-03-12 01:23:12 -0500498 cout << i + 1 << " ";
499 partitions[i].ShowData((state == gpt) || (state == hybrid));
srs5694f2efa7d2011-03-01 22:03:26 -0500500 } // if
501 cout.fill(' ');
502 } // for
srs5694f2efa7d2011-03-01 22:03:26 -0500503} // BasicMBRData::DisplayMBRData()
504
505// Displays the state, as a word, on stdout. Used for debugging & to
506// tell the user about the MBR state when the program launches....
507void BasicMBRData::ShowState(void) {
508 switch (state) {
509 case invalid:
510 cout << " MBR: not present\n";
511 break;
512 case gpt:
513 cout << " MBR: protective\n";
514 break;
515 case hybrid:
516 cout << " MBR: hybrid\n";
517 break;
518 case mbr:
519 cout << " MBR: MBR only\n";
520 break;
521 default:
522 cout << "\a MBR: unknown -- bug!\n";
523 break;
524 } // switch
525} // BasicMBRData::ShowState()
526
srs5694bf8950c2011-03-12 01:23:12 -0500527/************************
528 * *
529 * GPT Checks and fixes *
530 * *
531 ************************/
532
533// Perform a very rudimentary check for GPT data on the disk; searches for
534// the GPT signature in the main and backup metadata areas.
535// Returns 0 if GPT data not found, 1 if main data only is found, 2 if
536// backup only is found, 3 if both main and backup data are found, and
537// -1 if a disk error occurred.
538int BasicMBRData::CheckForGPT(void) {
539 int retval = 0, err;
540 char signature1[9], signature2[9];
541
542 if (myDisk != NULL) {
543 if (myDisk->OpenForRead() != 0) {
544 if (myDisk->Seek(1)) {
545 myDisk->Read(signature1, 8);
546 signature1[8] = '\0';
547 } else retval = -1;
548 if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) {
549 myDisk->Read(signature2, 8);
550 signature2[8] = '\0';
551 } else retval = -1;
552 if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0))
553 retval += 1;
554 if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0))
555 retval += 2;
556 } else {
557 retval = -1;
558 } // if/else
559 myDisk->Close();
560 } else retval = -1;
561 return retval;
562} // BasicMBRData::CheckForGPT()
563
564// Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk,
565// but only if GPT data are verified on the disk, and only for the sector(s)
566// with GPT signatures.
567// Returns 1 if operation completes successfully, 0 if not (returns 1 if
568// no GPT data are found on the disk).
569int BasicMBRData::BlankGPTData(void) {
570 int allOK = 1, err;
571 uint8_t blank[512];
572
573 memset(blank, 0, 512);
574 switch (CheckForGPT()) {
575 case -1:
576 allOK = 0;
577 break;
578 case 0:
579 break;
580 case 1:
581 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
582 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
583 allOK = 0;
584 myDisk->Close();
585 } else allOK = 0;
586 break;
587 case 2:
588 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
589 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
590 (myDisk->Write(blank, 512) == 512)))
591 allOK = 0;
592 myDisk->Close();
593 } else allOK = 0;
594 break;
595 case 3:
596 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
597 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
598 allOK = 0;
599 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
600 (myDisk->Write(blank, 512) == 512)))
601 allOK = 0;
602 myDisk->Close();
603 } else allOK = 0;
604 break;
605 default:
606 break;
607 } // switch()
608 return allOK;
609} // BasicMBRData::BlankGPTData
610
srs5694f2efa7d2011-03-01 22:03:26 -0500611/*********************************************************************
612 * *
613 * Functions that set or get disk metadata (CHS geometry, disk size, *
614 * etc.) *
615 * *
616 *********************************************************************/
617
srs5694bf8950c2011-03-12 01:23:12 -0500618// Read the CHS geometry using OS calls, or if that fails, set to
619// the most common value for big disks (255 heads, 63 sectors per
620// track, & however many cylinders that computes to).
621void BasicMBRData::ReadCHSGeom(void) {
srs5694a17fe692011-09-10 20:30:20 -0400622 int err;
623
srs5694bf8950c2011-03-12 01:23:12 -0500624 numHeads = myDisk->GetNumHeads();
625 numSecspTrack = myDisk->GetNumSecsPerTrack();
srs5694a17fe692011-09-10 20:30:20 -0400626 diskSize = myDisk->DiskSize(&err);
627 blockSize = myDisk->GetBlockSize();
srs5694bf8950c2011-03-12 01:23:12 -0500628 partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize);
629} // BasicMBRData::ReadCHSGeom()
630
631// Find the low and high used partition numbers (numbered from 0).
632// Return value is the number of partitions found. Note that the
633// *low and *high values are both set to 0 when no partitions
634// are found, as well as when a single partition in the first
635// position exists. Thus, the return value is the only way to
636// tell when no partitions exist.
637int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) {
638 uint32_t i;
639 int numFound = 0;
640
641 *low = MAX_MBR_PARTS + 1; // code for "not found"
642 *high = 0;
643 for (i = 0; i < MAX_MBR_PARTS; i++) {
644 if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists
645 *high = i; // since we're counting up, set the high value
646 // Set the low value only if it's not yet found...
647 if (*low == (MAX_MBR_PARTS + 1))
648 *low = i;
649 numFound++;
650 } // if
651 } // for
652
653 // Above will leave *low pointing to its "not found" value if no partitions
654 // are defined, so reset to 0 if this is the case....
655 if (*low == (MAX_MBR_PARTS + 1))
656 *low = 0;
657 return numFound;
658} // GPTData::GetPartRange()
srs5694f2efa7d2011-03-01 22:03:26 -0500659
660// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
661// was within the range that can be expressed by CHS (including 0, for an
662// empty partition), 0 if the value is outside that range, and -1 if chs is
663// invalid.
664int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
665 uint64_t cylinder, head, sector; // all numbered from 0
666 uint64_t remainder;
667 int retval = 1;
668 int done = 0;
669
670 if (chs != NULL) {
671 // Special case: In case of 0 LBA value, zero out CHS values....
672 if (lba == 0) {
673 chs[0] = chs[1] = chs[2] = UINT8_C(0);
674 done = 1;
675 } // if
676 // If LBA value is too large for CHS, max out CHS values....
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500677 if ((!done) && (lba >= ((uint64_t) numHeads * numSecspTrack * MAX_CYLINDERS))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500678 chs[0] = 254;
679 chs[1] = chs[2] = 255;
680 done = 1;
681 retval = 0;
682 } // if
683 // If neither of the above applies, compute CHS values....
684 if (!done) {
685 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
686 remainder = lba - (cylinder * numHeads * numSecspTrack);
687 head = remainder / numSecspTrack;
688 remainder -= head * numSecspTrack;
689 sector = remainder;
690 if (head < numHeads)
691 chs[0] = (uint8_t) head;
692 else
693 retval = 0;
694 if (sector < numSecspTrack) {
695 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
696 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
697 } else {
698 retval = 0;
699 } // if/else
700 } // if value is expressible and non-0
701 } else { // Invalid (NULL) chs pointer
702 retval = -1;
703 } // if CHS pointer valid
704 return (retval);
705} // BasicMBRData::LBAtoCHS()
706
Roderick W. Smith427c7992013-09-12 12:37:44 -0400707// Look for overlapping partitions. Also looks for a couple of non-error
708// conditions that the user should be told about.
srs5694bf8950c2011-03-12 01:23:12 -0500709// Returns the number of problems found
710int BasicMBRData::FindOverlaps(void) {
Roderick W. Smith427c7992013-09-12 12:37:44 -0400711 int i, j, numProbs = 0, numEE = 0, ProtectiveOnOne = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500712
713 for (i = 0; i < MAX_MBR_PARTS; i++) {
714 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
srs569423d8d542011-10-01 18:40:10 -0400715 if ((partitions[i].GetInclusion() != NONE) && (partitions[j].GetInclusion() != NONE) &&
srs5694bf8950c2011-03-12 01:23:12 -0500716 (partitions[i].DoTheyOverlap(partitions[j]))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500717 numProbs++;
718 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
719 << " overlap!\n";
720 } // if
721 } // for (j...)
srs5694bf8950c2011-03-12 01:23:12 -0500722 if (partitions[i].GetType() == 0xEE) {
srs5694f2efa7d2011-03-01 22:03:26 -0500723 numEE++;
Roderick W. Smith427c7992013-09-12 12:37:44 -0400724 if (partitions[i].GetStartLBA() == 1)
725 ProtectiveOnOne = 1;
srs5694f2efa7d2011-03-01 22:03:26 -0500726 } // if
727 } // for (i...)
Roderick W. Smith427c7992013-09-12 12:37:44 -0400728
srs5694f2efa7d2011-03-01 22:03:26 -0500729 if (numEE > 1)
730 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
731 << "in some OSes.\n";
Roderick W. Smith22e88b52014-02-17 12:07:02 -0500732 if (!ProtectiveOnOne && (numEE > 0))
Roderick W. Smith427c7992013-09-12 12:37:44 -0400733 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause "
734 << "problems\nin some OSes.\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500735
736 return numProbs;
srs5694bf8950c2011-03-12 01:23:12 -0500737} // BasicMBRData::FindOverlaps()
738
739// Returns the number of primary partitions, including the extended partition
740// required to hold any logical partitions found.
741int BasicMBRData::NumPrimaries(void) {
742 int i, numPrimaries = 0, logicalsFound = 0;
743
744 for (i = 0; i < MAX_MBR_PARTS; i++) {
745 if (partitions[i].GetLengthLBA() > 0) {
746 if (partitions[i].GetInclusion() == PRIMARY)
747 numPrimaries++;
748 if (partitions[i].GetInclusion() == LOGICAL)
749 logicalsFound = 1;
750 } // if
751 } // for
752 return (numPrimaries + logicalsFound);
753} // BasicMBRData::NumPrimaries()
754
755// Returns the number of logical partitions.
756int BasicMBRData::NumLogicals(void) {
757 int i, numLogicals = 0;
758
759 for (i = 0; i < MAX_MBR_PARTS; i++) {
760 if (partitions[i].GetInclusion() == LOGICAL)
761 numLogicals++;
762 } // for
763 return numLogicals;
764} // BasicMBRData::NumLogicals()
765
766// Returns the number of partitions (primaries plus logicals), NOT including
767// the extended partition required to house the logicals.
768int BasicMBRData::CountParts(void) {
769 int i, num = 0;
770
771 for (i = 0; i < MAX_MBR_PARTS; i++) {
772 if ((partitions[i].GetInclusion() == LOGICAL) ||
773 (partitions[i].GetInclusion() == PRIMARY))
774 num++;
775 } // for
776 return num;
777} // BasicMBRData::CountParts()
778
779// Updates the canBeLogical and canBePrimary flags for all the partitions.
780void BasicMBRData::UpdateCanBeLogical(void) {
781 int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR;
782 uint64_t firstLogical, lastLogical, lStart, pStart;
783
784 numPrimaries = NumPrimaries();
785 numLogicals = NumLogicals();
786 firstLogical = FirstLogicalLBA() - 1;
787 lastLogical = LastLogicalLBA();
788 for (i = 0; i < MAX_MBR_PARTS; i++) {
789 usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR);
790 if (usedAsEBR) {
791 partitions[i].SetCanBeLogical(0);
792 partitions[i].SetCanBePrimary(0);
793 } else if (partitions[i].GetLengthLBA() > 0) {
794 // First determine if it can be logical....
795 sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1);
796 lStart = partitions[i].GetStartLBA(); // start of potential logical part.
797 if ((lastLogical > 0) &&
798 ((sectorBefore == EBR) || (sectorBefore == NONE))) {
799 // Assume it can be logical, then search for primaries that make it
800 // not work and, if found, flag appropriately.
801 partitions[i].SetCanBeLogical(1);
802 for (j = 0; j < MAX_MBR_PARTS; j++) {
803 if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) {
804 pStart = partitions[j].GetStartLBA();
805 if (((pStart < lStart) && (firstLogical < pStart)) ||
806 ((pStart > lStart) && (firstLogical > pStart))) {
807 partitions[i].SetCanBeLogical(0);
808 } // if/else
809 } // if
810 } // for
811 } else {
812 if ((sectorBefore != EBR) && (sectorBefore != NONE))
813 partitions[i].SetCanBeLogical(0);
814 else
815 partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already
816 } // if/else
817 // Now determine if it can be primary. Start by assuming it can be...
818 partitions[i].SetCanBePrimary(1);
819 if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) {
820 partitions[i].SetCanBePrimary(0);
821 if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) &&
822 (numPrimaries == 4))
823 partitions[i].SetCanBePrimary(1);
824 } // if
825 if ((partitions[i].GetStartLBA() > (firstLogical + 1)) &&
826 (partitions[i].GetLastLBA() < lastLogical))
827 partitions[i].SetCanBePrimary(0);
828 } // else if
829 } // for
830} // BasicMBRData::UpdateCanBeLogical()
831
832// Returns the first sector occupied by any logical partition. Note that
833// this does NOT include the logical partition's EBR! Returns UINT32_MAX
834// if there are no logical partitions defined.
835uint64_t BasicMBRData::FirstLogicalLBA(void) {
836 int i;
837 uint64_t firstFound = UINT32_MAX;
838
839 for (i = 0; i < MAX_MBR_PARTS; i++) {
840 if ((partitions[i].GetInclusion() == LOGICAL) &&
841 (partitions[i].GetStartLBA() < firstFound)) {
842 firstFound = partitions[i].GetStartLBA();
843 } // if
844 } // for
845 return firstFound;
846} // BasicMBRData::FirstLogicalLBA()
847
848// Returns the last sector occupied by any logical partition, or 0 if
849// there are no logical partitions defined.
850uint64_t BasicMBRData::LastLogicalLBA(void) {
851 int i;
852 uint64_t lastFound = 0;
853
854 for (i = 0; i < MAX_MBR_PARTS; i++) {
855 if ((partitions[i].GetInclusion() == LOGICAL) &&
856 (partitions[i].GetLastLBA() > lastFound))
857 lastFound = partitions[i].GetLastLBA();
858 } // for
859 return lastFound;
860} // BasicMBRData::LastLogicalLBA()
861
862// Returns 1 if logical partitions are contiguous (have no primaries
863// in their midst), or 0 if one or more primaries exist between
864// logicals.
865int BasicMBRData::AreLogicalsContiguous(void) {
866 int allOK = 1, i = 0;
867 uint64_t firstLogical, lastLogical;
868
869 firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR
870 lastLogical = LastLogicalLBA();
871 if (lastLogical > 0) {
872 do {
873 if ((partitions[i].GetInclusion() == PRIMARY) &&
874 (partitions[i].GetStartLBA() >= firstLogical) &&
875 (partitions[i].GetStartLBA() <= lastLogical)) {
876 allOK = 0;
877 } // if
878 i++;
879 } while ((i < MAX_MBR_PARTS) && allOK);
880 } // if
881 return allOK;
882} // BasicMBRData::AreLogicalsContiguous()
883
884// Returns 1 if all partitions fit on the disk, given its size; 0 if any
885// partition is too big.
886int BasicMBRData::DoTheyFit(void) {
887 int i, allOK = 1;
888
889 for (i = 0; i < MAX_MBR_PARTS; i++) {
890 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) {
891 allOK = 0;
892 } // if
893 } // for
894 return allOK;
895} // BasicMBRData::DoTheyFit(void)
896
897// Returns 1 if there's at least one free sector immediately preceding
898// all partitions flagged as logical; 0 if any logical partition lacks
899// this space.
900int BasicMBRData::SpaceBeforeAllLogicals(void) {
901 int i = 0, allOK = 1;
902
903 do {
904 if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) {
905 allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR);
srs5694bf8950c2011-03-12 01:23:12 -0500906 } // if
907 i++;
908 } while (allOK && (i < MAX_MBR_PARTS));
909 return allOK;
910} // BasicMBRData::SpaceBeforeAllLogicals()
911
912// Returns 1 if the partitions describe a legal layout -- all logicals
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400913// are contiguous and have at least one preceding empty sector,
srs5694bf8950c2011-03-12 01:23:12 -0500914// the number of primaries is under 4 (or under 3 if there are any
915// logicals), there are no overlapping partitions, etc.
916// Does NOT assume that primaries are numbered 1-4; uses the
917// IsItPrimary() function of the MBRPart class to determine
918// primary status. Also does NOT consider partition order; there
919// can be gaps and it will still be considered legal.
920int BasicMBRData::IsLegal(void) {
921 int allOK = 1;
922
923 allOK = (FindOverlaps() == 0);
924 allOK = (allOK && (NumPrimaries() <= 4));
925 allOK = (allOK && AreLogicalsContiguous());
926 allOK = (allOK && DoTheyFit());
927 allOK = (allOK && SpaceBeforeAllLogicals());
928 return allOK;
929} // BasicMBRData::IsLegal()
930
Roderick W. Smith042f38a2013-08-31 17:40:15 -0400931// Returns 1 if the 0xEE partition in the protective/hybrid MBR is marked as
932// active/bootable.
933int BasicMBRData::IsEEActive(void) {
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400934 int i, IsActive = 0;
Roderick W. Smith042f38a2013-08-31 17:40:15 -0400935
936 for (i = 0; i < MAX_MBR_PARTS; i++) {
Roderick W. Smith427c7992013-09-12 12:37:44 -0400937 if ((partitions[i].GetStatus() & 0x80) && (partitions[i].GetType() == 0xEE))
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400938 IsActive = 1;
Roderick W. Smith042f38a2013-08-31 17:40:15 -0400939 }
940 return IsActive;
941} // BasicMBRData::IsEEActive()
942
srs5694bf8950c2011-03-12 01:23:12 -0500943// Finds the next in-use partition, starting with start (will return start
944// if it's in use). Returns -1 if no subsequent partition is in use.
945int BasicMBRData::FindNextInUse(int start) {
946 if (start >= MAX_MBR_PARTS)
947 start = -1;
948 while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE))
949 start++;
950 if ((start < 0) || (start >= MAX_MBR_PARTS))
951 start = -1;
952 return start;
953} // BasicMBRData::FindFirstLogical();
srs5694f2efa7d2011-03-01 22:03:26 -0500954
955/*****************************************************
956 * *
957 * Functions to create, delete, or change partitions *
958 * *
959 *****************************************************/
960
961// Empty all data. Meant mainly for calling by constructors, but it's also
962// used by the hybrid MBR functions in the GPTData class.
963void BasicMBRData::EmptyMBR(int clearBootloader) {
964 int i;
965
966 // Zero out the boot loader section, the disk signature, and the
967 // 2-byte nulls area only if requested to do so. (This is the
968 // default.)
969 if (clearBootloader == 1) {
970 EmptyBootloader();
971 } // if
972
973 // Blank out the partitions
974 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500975 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -0500976 } // for
977 MBRSignature = MBR_SIGNATURE;
srs5694bf8950c2011-03-12 01:23:12 -0500978 state = mbr;
srs5694f2efa7d2011-03-01 22:03:26 -0500979} // BasicMBRData::EmptyMBR()
980
981// Blank out the boot loader area. Done with the initial MBR-to-GPT
982// conversion, since MBR boot loaders don't understand GPT, and so
983// need to be replaced....
984void BasicMBRData::EmptyBootloader(void) {
985 int i;
986
987 for (i = 0; i < 440; i++)
988 code[i] = 0;
989 nulls = 0;
990} // BasicMBRData::EmptyBootloader
991
srs5694bf8950c2011-03-12 01:23:12 -0500992// Create a partition of the specified number based on the passed
993// partition. This function does *NO* error checking, so it's possible
srs5694f2efa7d2011-03-01 22:03:26 -0500994// to seriously screw up a partition table using this function!
995// Note: This function should NOT be used to create the 0xEE partition
996// in a conventional GPT configuration, since that partition has
997// specific size requirements that this function won't handle. It may
998// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
999// since those toss the rulebook away anyhow....
srs5694bf8950c2011-03-12 01:23:12 -05001000void BasicMBRData::AddPart(int num, const MBRPart& newPart) {
1001 partitions[num] = newPart;
1002} // BasicMBRData::AddPart()
1003
1004// Create a partition of the specified number, starting LBA, and
1005// length. This function does almost no error checking, so it's possible
1006// to seriously screw up a partition table using this function!
1007// Note: This function should NOT be used to create the 0xEE partition
1008// in a conventional GPT configuration, since that partition has
1009// specific size requirements that this function won't handle. It may
1010// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
1011// since those toss the rulebook away anyhow....
1012void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) {
1013 if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) {
1014 partitions[num].Empty();
1015 partitions[num].SetType(type);
1016 partitions[num].SetLocation(start, length);
1017 if (num < 4)
1018 partitions[num].SetInclusion(PRIMARY);
1019 else
1020 partitions[num].SetInclusion(LOGICAL);
srs5694f2efa7d2011-03-01 22:03:26 -05001021 SetPartBootable(num, bootable);
srs5694bf8950c2011-03-12 01:23:12 -05001022 } // if valid partition number & size
srs5694f2efa7d2011-03-01 22:03:26 -05001023} // BasicMBRData::MakePart()
1024
1025// Set the partition's type code.
1026// Returns 1 if successful, 0 if not (invalid partition number)
1027int BasicMBRData::SetPartType(int num, int type) {
1028 int allOK = 1;
1029
1030 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001031 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
1032 allOK = partitions[num].SetType(type);
srs5694f2efa7d2011-03-01 22:03:26 -05001033 } else allOK = 0;
1034 } else allOK = 0;
1035 return allOK;
1036} // BasicMBRData::SetPartType()
1037
1038// Set (or remove) the partition's bootable flag. Setting it is the
1039// default; pass 0 as bootable to remove the flag.
1040// Returns 1 if successful, 0 if not (invalid partition number)
1041int BasicMBRData::SetPartBootable(int num, int bootable) {
1042 int allOK = 1;
1043
1044 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001045 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001046 if (bootable == 0)
srs5694bf8950c2011-03-12 01:23:12 -05001047 partitions[num].SetStatus(UINT8_C(0x00));
srs5694f2efa7d2011-03-01 22:03:26 -05001048 else
srs5694bf8950c2011-03-12 01:23:12 -05001049 partitions[num].SetStatus(UINT8_C(0x80));
srs5694f2efa7d2011-03-01 22:03:26 -05001050 } else allOK = 0;
1051 } else allOK = 0;
1052 return allOK;
1053} // BasicMBRData::SetPartBootable()
1054
1055// Create a partition that fills the most available space. Returns
1056// 1 if partition was created, 0 otherwise. Intended for use in
1057// creating hybrid MBRs.
1058int BasicMBRData::MakeBiggestPart(int i, int type) {
srs5694bf8950c2011-03-12 01:23:12 -05001059 uint64_t start = UINT64_C(1); // starting point for each search
1060 uint64_t firstBlock; // first block in a segment
1061 uint64_t lastBlock; // last block in a segment
1062 uint64_t segmentSize; // size of segment in blocks
1063 uint64_t selectedSegment = UINT64_C(0); // location of largest segment
1064 uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks
srs5694f2efa7d2011-03-01 22:03:26 -05001065 int found = 0;
Roderick W. Smith427c7992013-09-12 12:37:44 -04001066 string anything;
srs5694f2efa7d2011-03-01 22:03:26 -05001067
1068 do {
1069 firstBlock = FindFirstAvailable(start);
srs5694bf8950c2011-03-12 01:23:12 -05001070 if (firstBlock > UINT64_C(0)) { // something's free...
srs5694f2efa7d2011-03-01 22:03:26 -05001071 lastBlock = FindLastInFree(firstBlock);
srs5694bf8950c2011-03-12 01:23:12 -05001072 segmentSize = lastBlock - firstBlock + UINT64_C(1);
srs5694f2efa7d2011-03-01 22:03:26 -05001073 if (segmentSize > selectedSize) {
1074 selectedSize = segmentSize;
1075 selectedSegment = firstBlock;
1076 } // if
1077 start = lastBlock + 1;
1078 } // if
1079 } while (firstBlock != 0);
srs5694bf8950c2011-03-12 01:23:12 -05001080 if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001081 found = 1;
1082 MakePart(i, selectedSegment, selectedSize, type, 0);
1083 } else {
1084 found = 0;
1085 } // if/else
1086 return found;
1087} // BasicMBRData::MakeBiggestPart(int i)
1088
1089// Delete partition #i
1090void BasicMBRData::DeletePartition(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001091 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -05001092} // BasicMBRData::DeletePartition()
1093
srs5694bf8950c2011-03-12 01:23:12 -05001094// Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity
1095// checks to ensure the table remains legal.
1096// Returns 1 on success, 0 on failure.
1097int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) {
1098 int allOK = 1, origValue;
1099
1100 if (IsLegal()) {
1101 if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) {
1102 origValue = partitions[num].GetInclusion();
1103 partitions[num].SetInclusion(inclStatus);
1104 if (!IsLegal()) {
1105 partitions[num].SetInclusion(origValue);
1106 cerr << "Specified change is not legal! Aborting change!\n";
1107 } // if
1108 } else {
1109 cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n";
1110 } // if/else
1111 } else {
1112 cerr << "Partition table is not currently in a valid state. Aborting change!\n";
1113 allOK = 0;
1114 } // if/else
1115 return allOK;
1116} // BasicMBRData::SetInclusionwChecks()
1117
srs5694f2efa7d2011-03-01 22:03:26 -05001118// Recomputes the CHS values for the specified partition and adjusts the value.
1119// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
1120// protective partitions, but this is required by some buggy BIOSes, so I'm
1121// providing a function to do this deliberately at the user's command.
1122// This function does nothing if the partition's length is 0.
1123void BasicMBRData::RecomputeCHS(int partNum) {
srs5694bf8950c2011-03-12 01:23:12 -05001124 partitions[partNum].RecomputeCHS();
srs5694f2efa7d2011-03-01 22:03:26 -05001125} // BasicMBRData::RecomputeCHS()
1126
srs5694699941e2011-03-21 21:33:57 -04001127// Sorts the partitions starting with partition #start. This function
1128// does NOT pay attention to primary/logical assignment, which is
1129// critical when writing the partitions.
srs5694bf8950c2011-03-12 01:23:12 -05001130void BasicMBRData::SortMBR(int start) {
srs5694c2f6e0c2011-03-16 02:42:33 -04001131 if ((start < MAX_MBR_PARTS) && (start >= 0))
1132 sort(partitions + start, partitions + MAX_MBR_PARTS);
1133} // BasicMBRData::SortMBR()
srs5694bf8950c2011-03-12 01:23:12 -05001134
1135// Delete any partitions that are too big to fit on the disk
1136// or that are too big for MBR (32-bit limits).
srs569400b6d7a2011-06-26 22:40:06 -04001137// This deletes the partitions by setting values to 0, not just
1138// by setting them as being omitted.
srs5694bf8950c2011-03-12 01:23:12 -05001139// Returns the number of partitions deleted in this way.
1140int BasicMBRData::DeleteOversizedParts() {
1141 int num = 0, i;
1142
1143 for (i = 0; i < MAX_MBR_PARTS; i++) {
1144 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) ||
1145 (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) {
srs569400b6d7a2011-06-26 22:40:06 -04001146 cerr << "\aWarning: Deleting oversized partition #" << i + 1 << "! Start = "
1147 << partitions[i].GetStartLBA() << ", length = " << partitions[i].GetLengthLBA() << "\n";
srs5694bf8950c2011-03-12 01:23:12 -05001148 partitions[i].Empty();
1149 num++;
1150 } // if
1151 } // for
1152 return num;
1153} // BasicMBRData::DeleteOversizedParts()
1154
1155// Search for and delete extended partitions.
1156// Returns the number of partitions deleted.
1157int BasicMBRData::DeleteExtendedParts() {
1158 int i, numDeleted = 0;
1159 uint8_t type;
1160
1161 for (i = 0; i < MAX_MBR_PARTS; i++) {
1162 type = partitions[i].GetType();
1163 if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) &&
1164 (partitions[i].GetLengthLBA() > 0)) {
1165 partitions[i].Empty();
1166 numDeleted++;
1167 } // if
1168 } // for
1169 return numDeleted;
1170} // BasicMBRData::DeleteExtendedParts()
1171
1172// Finds any overlapping partitions and omits the smaller of the two.
1173void BasicMBRData::OmitOverlaps() {
1174 int i, j;
1175
1176 for (i = 0; i < MAX_MBR_PARTS; i++) {
1177 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
1178 if ((partitions[i].GetInclusion() != NONE) &&
1179 partitions[i].DoTheyOverlap(partitions[j])) {
1180 if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA())
1181 partitions[i].SetInclusion(NONE);
1182 else
1183 partitions[j].SetInclusion(NONE);
srs5694f2efa7d2011-03-01 22:03:26 -05001184 } // if
srs5694bf8950c2011-03-12 01:23:12 -05001185 } // for (j...)
1186 } // for (i...)
1187} // BasicMBRData::OmitOverlaps()
1188
srs5694bf8950c2011-03-12 01:23:12 -05001189// Convert as many partitions into logicals as possible, except for
1190// the first partition, if possible.
1191void BasicMBRData::MaximizeLogicals() {
1192 int earliestPart = 0, earliestPartWas = NONE, i;
1193
1194 for (i = MAX_MBR_PARTS - 1; i >= 0; i--) {
1195 UpdateCanBeLogical();
1196 earliestPart = i;
1197 if (partitions[i].CanBeLogical()) {
1198 partitions[i].SetInclusion(LOGICAL);
1199 } else if (partitions[i].CanBePrimary()) {
1200 partitions[i].SetInclusion(PRIMARY);
1201 } else {
1202 partitions[i].SetInclusion(NONE);
1203 } // if/elseif/else
1204 } // for
1205 // If we have spare primaries, convert back the earliest partition to
1206 // its original state....
1207 if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL))
1208 partitions[earliestPart].SetInclusion(earliestPartWas);
1209} // BasicMBRData::MaximizeLogicals()
1210
1211// Add primaries up to the maximum allowed, from the omitted category.
1212void BasicMBRData::MaximizePrimaries() {
1213 int num, i = 0;
1214
1215 num = NumPrimaries();
1216 while ((num < 4) && (i < MAX_MBR_PARTS)) {
1217 if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) {
1218 partitions[i].SetInclusion(PRIMARY);
srs5694699941e2011-03-21 21:33:57 -04001219 num++;
1220 UpdateCanBeLogical();
srs5694bf8950c2011-03-12 01:23:12 -05001221 } // if
1222 i++;
1223 } // while
1224} // BasicMBRData::MaximizePrimaries()
1225
1226// Remove primary partitions in excess of 4, starting with the later ones,
1227// in terms of the array location....
1228void BasicMBRData::TrimPrimaries(void) {
Roderick W. Smith84aaff62014-02-17 16:17:11 -05001229 int numToDelete, i = MAX_MBR_PARTS - 1;
srs5694bf8950c2011-03-12 01:23:12 -05001230
1231 numToDelete = NumPrimaries() - 4;
1232 while ((numToDelete > 0) && (i >= 0)) {
1233 if (partitions[i].GetInclusion() == PRIMARY) {
1234 partitions[i].SetInclusion(NONE);
1235 numToDelete--;
1236 } // if
1237 i--;
1238 } // while (numToDelete > 0)
1239} // BasicMBRData::TrimPrimaries()
1240
1241// Locates primary partitions located between logical partitions and
1242// either converts the primaries into logicals (if possible) or omits
1243// them.
1244void BasicMBRData::MakeLogicalsContiguous(void) {
1245 uint64_t firstLogicalLBA, lastLogicalLBA;
1246 int i;
1247
1248 firstLogicalLBA = FirstLogicalLBA();
1249 lastLogicalLBA = LastLogicalLBA();
1250 for (i = 0; i < MAX_MBR_PARTS; i++) {
1251 if ((partitions[i].GetInclusion() == PRIMARY) &&
1252 (partitions[i].GetStartLBA() >= firstLogicalLBA) &&
1253 (partitions[i].GetLastLBA() <= lastLogicalLBA)) {
1254 if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE)
1255 partitions[i].SetInclusion(LOGICAL);
1256 else
1257 partitions[i].SetInclusion(NONE);
1258 } // if
1259 } // for
1260} // BasicMBRData::MakeLogicalsContiguous()
1261
1262// If MBR data aren't legal, adjust primary/logical assignments and,
1263// if necessary, drop partitions, to make the data legal.
1264void BasicMBRData::MakeItLegal(void) {
1265 if (!IsLegal()) {
1266 DeleteOversizedParts();
srs5694bf8950c2011-03-12 01:23:12 -05001267 MaximizeLogicals();
1268 MaximizePrimaries();
1269 if (!AreLogicalsContiguous())
1270 MakeLogicalsContiguous();
1271 if (NumPrimaries() > 4)
1272 TrimPrimaries();
1273 OmitOverlaps();
1274 } // if
1275} // BasicMBRData::MakeItLegal()
1276
1277// Removes logical partitions and deactivated partitions from first four
1278// entries (primary space).
1279// Returns the number of partitions moved.
1280int BasicMBRData::RemoveLogicalsFromFirstFour(void) {
1281 int i, j = 4, numMoved = 0, swapped = 0;
1282 MBRPart temp;
1283
1284 for (i = 0; i < 4; i++) {
1285 if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) {
1286 j = 4;
1287 swapped = 0;
1288 do {
1289 if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) {
1290 temp = partitions[j];
1291 partitions[j] = partitions[i];
1292 partitions[i] = temp;
1293 swapped = 1;
1294 numMoved++;
1295 } // if
1296 j++;
1297 } while ((j < MAX_MBR_PARTS) && !swapped);
1298 if (j >= MAX_MBR_PARTS)
1299 cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n";
1300 } // if
1301 } // for i...
1302 return numMoved;
1303} // BasicMBRData::RemoveLogicalsFromFirstFour()
1304
1305// Move all primaries into the first four partition spaces
1306// Returns the number of partitions moved.
1307int BasicMBRData::MovePrimariesToFirstFour(void) {
1308 int i, j = 0, numMoved = 0, swapped = 0;
1309 MBRPart temp;
1310
1311 for (i = 4; i < MAX_MBR_PARTS; i++) {
1312 if (partitions[i].GetInclusion() == PRIMARY) {
1313 j = 0;
1314 swapped = 0;
1315 do {
1316 if (partitions[j].GetInclusion() != PRIMARY) {
1317 temp = partitions[j];
1318 partitions[j] = partitions[i];
1319 partitions[i] = temp;
1320 swapped = 1;
1321 numMoved++;
1322 } // if
1323 j++;
1324 } while ((j < 4) && !swapped);
1325 } // if
1326 } // for
1327 return numMoved;
1328} // BasicMBRData::MovePrimariesToFirstFour()
1329
1330// Create an extended partition, if necessary, to hold the logical partitions.
1331// This function also sorts the primaries into the first four positions of
1332// the table.
1333// Returns 1 on success, 0 on failure.
1334int BasicMBRData::CreateExtended(void) {
1335 int allOK = 1, i = 0, swapped = 0;
1336 MBRPart temp;
1337
1338 if (IsLegal()) {
1339 // Move logicals out of primary space...
1340 RemoveLogicalsFromFirstFour();
1341 // Move primaries out of logical space...
1342 MovePrimariesToFirstFour();
1343
1344 // Create the extended partition
1345 if (NumLogicals() > 0) {
1346 SortMBR(4); // sort starting from 4 -- that is, logicals only
1347 temp.Empty();
1348 temp.SetStartLBA(FirstLogicalLBA() - 1);
1349 temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2);
1350 temp.SetType(0x0f, 1);
1351 temp.SetInclusion(PRIMARY);
1352 do {
1353 if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) {
1354 partitions[i] = temp;
1355 swapped = 1;
1356 } // if
1357 i++;
1358 } while ((i < 4) && !swapped);
1359 if (!swapped) {
1360 cerr << "Could not create extended partition; no room in primary table!\n";
1361 allOK = 0;
1362 } // if
1363 } // if (NumLogicals() > 0)
1364 } else allOK = 0;
1365 // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found
1366 // along with an extended partition
1367 for (i = 0; i < MAX_MBR_PARTS; i++)
1368 if (swapped && partitions[i].GetType() == 0xEE)
1369 allOK = 0;
1370 return allOK;
1371} // BasicMBRData::CreateExtended()
srs5694f2efa7d2011-03-01 22:03:26 -05001372
1373/****************************************
1374 * *
1375 * Functions to find data on free space *
1376 * *
1377 ****************************************/
1378
1379// Finds the first free space on the disk from start onward; returns 0
1380// if none available....
srs5694bf8950c2011-03-12 01:23:12 -05001381uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) {
1382 uint64_t first;
1383 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001384 int firstMoved;
1385
Roderick W. Smith427c7992013-09-12 12:37:44 -04001386 if ((start >= (UINT32_MAX - 1)) || (start >= (diskSize - 1)))
1387 return 0;
1388
srs5694f2efa7d2011-03-01 22:03:26 -05001389 first = start;
1390
1391 // ...now search through all partitions; if first is within an
1392 // existing partition, move it to the next sector after that
1393 // partition and repeat. If first was moved, set firstMoved
1394 // flag; repeat until firstMoved is not set, so as to catch
1395 // cases where partitions are out of sequential order....
1396 do {
1397 firstMoved = 0;
1398 for (i = 0; i < 4; i++) {
1399 // Check if it's in the existing partition
srs5694bf8950c2011-03-12 01:23:12 -05001400 if ((first >= partitions[i].GetStartLBA()) &&
1401 (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) {
1402 first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001403 firstMoved = 1;
1404 } // if
1405 } // for
1406 } while (firstMoved == 1);
srs5694bf8950c2011-03-12 01:23:12 -05001407 if ((first >= diskSize) || (first > UINT32_MAX))
srs5694f2efa7d2011-03-01 22:03:26 -05001408 first = 0;
1409 return (first);
1410} // BasicMBRData::FindFirstAvailable()
1411
1412// Finds the last free sector on the disk from start forward.
srs5694bf8950c2011-03-12 01:23:12 -05001413uint64_t BasicMBRData::FindLastInFree(uint64_t start) {
1414 uint64_t nearestStart;
1415 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001416
1417 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694bf8950c2011-03-12 01:23:12 -05001418 nearestStart = diskSize - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001419 else
1420 nearestStart = UINT32_MAX - 1;
srs5694699941e2011-03-21 21:33:57 -04001421
srs5694f2efa7d2011-03-01 22:03:26 -05001422 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001423 if ((nearestStart > partitions[i].GetStartLBA()) &&
1424 (partitions[i].GetStartLBA() > start)) {
1425 nearestStart = partitions[i].GetStartLBA() - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001426 } // if
1427 } // for
1428 return (nearestStart);
1429} // BasicMBRData::FindLastInFree()
1430
1431// Finds the first free sector on the disk from start backward.
srs5694bf8950c2011-03-12 01:23:12 -05001432uint64_t BasicMBRData::FindFirstInFree(uint64_t start) {
1433 uint64_t bestLastLBA, thisLastLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001434 int i;
1435
1436 bestLastLBA = 1;
1437 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001438 thisLastLBA = partitions[i].GetLastLBA() + 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001439 if (thisLastLBA > 0)
1440 thisLastLBA--;
1441 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
1442 bestLastLBA = thisLastLBA + 1;
1443 } // for
1444 return (bestLastLBA);
1445} // BasicMBRData::FindFirstInFree()
1446
srs569423d8d542011-10-01 18:40:10 -04001447// Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID.
1448// Note: If the sector immediately before a logical partition is in use by
1449// another partition, this function returns PRIMARY or LOGICAL for that
1450// sector, rather than EBR.
srs5694bf8950c2011-03-12 01:23:12 -05001451int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) {
1452 int i = 0, usedAs = NONE;
srs5694f2efa7d2011-03-01 22:03:26 -05001453
srs5694bf8950c2011-03-12 01:23:12 -05001454 do {
1455 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector))
1456 usedAs = partitions[i].GetInclusion();
1457 if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL))
1458 usedAs = EBR;
1459 if (sector == 0)
1460 usedAs = EBR;
1461 if (sector >= diskSize)
1462 usedAs = INVALID;
1463 i++;
srs569423d8d542011-10-01 18:40:10 -04001464 } while ((i < topPartNum) && ((usedAs == NONE) || (usedAs == EBR)));
srs5694bf8950c2011-03-12 01:23:12 -05001465 return usedAs;
1466} // BasicMBRData::SectorUsedAs()
1467
srs5694f2efa7d2011-03-01 22:03:26 -05001468/******************************************************
1469 * *
1470 * Functions that extract data on specific partitions *
1471 * *
1472 ******************************************************/
1473
1474uint8_t BasicMBRData::GetStatus(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001475 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001476 uint8_t retval;
1477
1478 thePart = GetPartition(i);
1479 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001480 retval = thePart->GetStatus();
srs5694f2efa7d2011-03-01 22:03:26 -05001481 else
1482 retval = UINT8_C(0);
1483 return retval;
1484} // BasicMBRData::GetStatus()
1485
1486uint8_t BasicMBRData::GetType(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001487 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001488 uint8_t retval;
1489
1490 thePart = GetPartition(i);
1491 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001492 retval = thePart->GetType();
srs5694f2efa7d2011-03-01 22:03:26 -05001493 else
1494 retval = UINT8_C(0);
1495 return retval;
1496} // BasicMBRData::GetType()
1497
srs5694bf8950c2011-03-12 01:23:12 -05001498uint64_t BasicMBRData::GetFirstSector(int i) {
1499 MBRPart* thePart;
1500 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001501
1502 thePart = GetPartition(i);
1503 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001504 retval = thePart->GetStartLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001505 } else
1506 retval = UINT32_C(0);
1507 return retval;
1508} // BasicMBRData::GetFirstSector()
1509
srs5694bf8950c2011-03-12 01:23:12 -05001510uint64_t BasicMBRData::GetLength(int i) {
1511 MBRPart* thePart;
1512 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001513
1514 thePart = GetPartition(i);
1515 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001516 retval = thePart->GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001517 } else
srs5694bf8950c2011-03-12 01:23:12 -05001518 retval = UINT64_C(0);
srs5694f2efa7d2011-03-01 22:03:26 -05001519 return retval;
1520} // BasicMBRData::GetLength()
1521
1522/***********************
1523 * *
1524 * Protected functions *
1525 * *
1526 ***********************/
1527
1528// Return a pointer to a primary or logical partition, or NULL if
1529// the partition is out of range....
srs5694bf8950c2011-03-12 01:23:12 -05001530MBRPart* BasicMBRData::GetPartition(int i) {
1531 MBRPart* thePart = NULL;
srs5694f2efa7d2011-03-01 22:03:26 -05001532
1533 if ((i >= 0) && (i < MAX_MBR_PARTS))
1534 thePart = &partitions[i];
1535 return thePart;
1536} // GetPartition()
srs5694bf8950c2011-03-12 01:23:12 -05001537
1538/*******************************************
1539 * *
1540 * Functions that involve user interaction *
1541 * *
1542 *******************************************/
1543
1544// Present the MBR operations menu. Note that the 'w' option does not
1545// immediately write data; that's handled by the calling function.
1546// Returns the number of partitions defined on exit, or -1 if the
1547// user selected the 'q' option. (Thus, the caller should save data
1548// if the return value is >0, or possibly >=0 depending on intentions.)
1549int BasicMBRData::DoMenu(const string& prompt) {
srs5694bf8950c2011-03-12 01:23:12 -05001550 int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0;
srs56946aae2a92011-06-10 01:16:51 -04001551 unsigned int hexCode;
1552 string tempStr;
srs5694bf8950c2011-03-12 01:23:12 -05001553
1554 do {
1555 cout << prompt;
srs56945a608532011-03-17 13:53:01 -04001556 switch (ReadString()[0]) {
1557 case '\0':
srs5694a6297b82012-03-25 16:13:16 -04001558 goOn = cin.good();
srs5694bf8950c2011-03-12 01:23:12 -05001559 break;
1560 case 'a': case 'A':
1561 num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1;
1562 if (partitions[num].GetInclusion() != NONE)
1563 partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80);
1564 break;
1565 case 'c': case 'C':
1566 for (num = 0; num < MAX_MBR_PARTS; num++)
1567 RecomputeCHS(num);
1568 break;
1569 case 'l': case 'L':
1570 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1;
1571 SetInclusionwChecks(num, LOGICAL);
1572 break;
1573 case 'o': case 'O':
1574 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1;
1575 SetInclusionwChecks(num, NONE);
1576 break;
1577 case 'p': case 'P':
1578 if (!haveShownInfo) {
1579 cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical "
1580 << "status,\n** unlike in most MBR partitioning tools!\n\a";
1581 cout << "\n** Extended partitions are not displayed, but will be generated "
1582 << "as required.\n";
1583 haveShownInfo = 1;
1584 } // if
1585 DisplayMBRData();
1586 break;
1587 case 'q': case 'Q':
1588 cout << "This will abandon your changes. Are you sure? ";
1589 if (GetYN() == 'Y') {
1590 goOn = 0;
1591 quitting = 1;
1592 } // if
1593 break;
1594 case 'r': case 'R':
1595 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1;
1596 SetInclusionwChecks(num, PRIMARY);
1597 break;
1598 case 's': case 'S':
1599 SortMBR();
1600 break;
1601 case 't': case 'T':
1602 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1;
srs56946aae2a92011-06-10 01:16:51 -04001603 hexCode = 0x00;
srs5694bf8950c2011-03-12 01:23:12 -05001604 if (partitions[num].GetLengthLBA() > 0) {
srs5694bf8950c2011-03-12 01:23:12 -05001605 while ((hexCode <= 0) || (hexCode > 255)) {
1606 cout << "Enter an MBR hex code: ";
srs56946aae2a92011-06-10 01:16:51 -04001607 tempStr = ReadString();
1608 if (IsHex(tempStr))
1609 sscanf(tempStr.c_str(), "%x", &hexCode);
srs5694bf8950c2011-03-12 01:23:12 -05001610 } // while
1611 partitions[num].SetType(hexCode);
1612 } // if
1613 break;
1614 case 'w': case 'W':
1615 goOn = 0;
1616 break;
1617 default:
1618 ShowCommands();
1619 break;
1620 } // switch
1621 } while (goOn);
1622 if (quitting)
1623 retval = -1;
1624 else
1625 retval = CountParts();
1626 return (retval);
1627} // BasicMBRData::DoMenu()
1628
1629void BasicMBRData::ShowCommands(void) {
1630 cout << "a\ttoggle the active/boot flag\n";
1631 cout << "c\trecompute all CHS values\n";
1632 cout << "l\tset partition as logical\n";
1633 cout << "o\tomit partition\n";
1634 cout << "p\tprint the MBR partition table\n";
1635 cout << "q\tquit without saving changes\n";
1636 cout << "r\tset partition as primary\n";
1637 cout << "s\tsort MBR partitions\n";
1638 cout << "t\tchange partition type code\n";
1639 cout << "w\twrite the MBR partition table to disk and exit\n";
1640} // BasicMBRData::ShowCommands()