blob: 23f35b406614e020a08c771a8a38ed0a979e47be [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;
263 partNum -= 1;
264 } // if
265 } // for
266 EbrLocations[partNum] = offset;
srs5694f2efa7d2011-03-01 22:03:26 -0500267 if (myDisk->Seek(offset) == 0) { // seek to EBR record
268 cerr << "Unable to seek to " << offset << "! Aborting!\n";
srs569423d8d542011-10-01 18:40:10 -0400269 allOK = -1;
srs5694f2efa7d2011-03-01 22:03:26 -0500270 }
271 if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
272 cerr << "Error seeking to or reading logical partition data from " << offset
srs569423d8d542011-10-01 18:40:10 -0400273 << "!\nSome logical partitions may be missing!\n";
274 allOK = -1;
srs5694f2efa7d2011-03-01 22:03:26 -0500275 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
276 ReverseBytes(&ebr.MBRSignature, 2);
277 ReverseBytes(&ebr.partitions[0].firstLBA, 4);
278 ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
279 ReverseBytes(&ebr.partitions[1].firstLBA, 4);
280 ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
281 } // if/else/if
282
283 if (ebr.MBRSignature != MBR_SIGNATURE) {
srs569423d8d542011-10-01 18:40:10 -0400284 allOK = -1;
285 cerr << "EBR signature for logical partition invalid; read 0x";
srs5694f2efa7d2011-03-01 22:03:26 -0500286 cerr.fill('0');
287 cerr.width(4);
288 cerr.setf(ios::uppercase);
289 cerr << hex << ebr.MBRSignature << ", but should be 0x";
290 cerr.width(4);
291 cerr << MBR_SIGNATURE << dec << "\n";
292 cerr.fill(' ');
293 } // if
294
srs569423d8d542011-10-01 18:40:10 -0400295 if ((partNum >= 0) && (partNum < MAX_MBR_PARTS) && (allOK > 0)) {
296 // Sometimes an EBR points directly to another EBR, rather than defining
297 // a logical partition and then pointing to another EBR. Thus, we skip
298 // the logical partition when this is the case....
299 ebrType = ebr.partitions[0].partitionType;
300 if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) {
Aurimas Liutikasbdbab022017-03-07 09:50:36 -0800301 cout << "EBR describes a logical partition!\n";
srs569423d8d542011-10-01 18:40:10 -0400302 offset = extendedStart + ebr.partitions[0].firstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500303 } else {
srs569423d8d542011-10-01 18:40:10 -0400304 // Copy over the basic data....
305 partitions[partNum] = ebr.partitions[0];
306 // Adjust the start LBA, since it's encoded strangely....
307 partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + offset);
308 partitions[partNum].SetInclusion(LOGICAL);
309
310 // Find the next partition (if there is one)
311 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum < (MAX_MBR_PARTS - 1))) {
312 offset = extendedStart + ebr.partitions[1].firstLBA;
313 partNum++;
314 } else {
315 another = 0;
316 } // if another partition
317 } // if/else
318 } // if
319 } // while()
320 return (partNum * allOK);
srs5694f2efa7d2011-03-01 22:03:26 -0500321} // BasicMBRData::ReadLogicalPart()
322
323// Write the MBR data to the default defined device. This writes both the
324// MBR itself and any defined logical partitions, provided there's an
325// MBR extended partition.
326int BasicMBRData::WriteMBRData(void) {
327 int allOK = 1;
328
329 if (myDisk != NULL) {
330 if (myDisk->OpenForWrite() != 0) {
331 allOK = WriteMBRData(myDisk);
srs5694bf8950c2011-03-12 01:23:12 -0500332 cout << "Done writing data!\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500333 } else {
334 allOK = 0;
335 } // if/else
336 myDisk->Close();
337 } else allOK = 0;
338 return allOK;
339} // BasicMBRData::WriteMBRData(void)
340
341// Save the MBR data to a file. This writes both the
srs5694bf8950c2011-03-12 01:23:12 -0500342// MBR itself and any defined logical partitions.
srs5694f2efa7d2011-03-01 22:03:26 -0500343int BasicMBRData::WriteMBRData(DiskIO *theDisk) {
srs5694bf8950c2011-03-12 01:23:12 -0500344 int i, j, partNum, next, allOK = 1, moreLogicals = 0;
345 uint64_t extFirstLBA = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500346 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
347 TempMBR tempMBR;
348
srs5694bf8950c2011-03-12 01:23:12 -0500349 allOK = CreateExtended();
350 if (allOK) {
351 // First write the main MBR data structure....
352 memcpy(tempMBR.code, code, 440);
353 tempMBR.diskSignature = diskSignature;
354 tempMBR.nulls = nulls;
355 tempMBR.MBRSignature = MBRSignature;
356 for (i = 0; i < 4; i++) {
357 partitions[i].StoreInStruct(&tempMBR.partitions[i]);
358 if (partitions[i].GetType() == 0x0f) {
359 extFirstLBA = partitions[i].GetStartLBA();
360 moreLogicals = 1;
361 } // if
362 } // for i...
363 } // if
364 allOK = allOK && WriteMBRData(tempMBR, theDisk, 0);
srs5694f2efa7d2011-03-01 22:03:26 -0500365
366 // Set up tempMBR with some constant data for logical partitions...
367 tempMBR.diskSignature = 0;
368 for (i = 2; i < 4; i++) {
369 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
370 tempMBR.partitions[i].partitionType = 0x00;
371 for (j = 0; j < 3; j++) {
372 tempMBR.partitions[i].firstSector[j] = 0;
373 tempMBR.partitions[i].lastSector[j] = 0;
374 } // for j
375 } // for i
376
srs5694bf8950c2011-03-12 01:23:12 -0500377 partNum = FindNextInUse(4);
srs5694f2efa7d2011-03-01 22:03:26 -0500378 writeEbrTo = (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500379 // Write logicals...
srs569423d8d542011-10-01 18:40:10 -0400380 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
srs5694bf8950c2011-03-12 01:23:12 -0500381 partitions[partNum].StoreInStruct(&tempMBR.partitions[0]);
382 tempMBR.partitions[0].firstLBA = 1;
srs5694f2efa7d2011-03-01 22:03:26 -0500383 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
srs5694bf8950c2011-03-12 01:23:12 -0500384 next = FindNextInUse(partNum + 1);
385 if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) {
srs5694f2efa7d2011-03-01 22:03:26 -0500386 tempMBR.partitions[1].partitionType = 0x0f;
srs5694bf8950c2011-03-12 01:23:12 -0500387 tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1);
388 tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 1);
srs5694f2efa7d2011-03-01 22:03:26 -0500389 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
390 (uint8_t *) &tempMBR.partitions[1].firstSector);
391 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
392 (uint8_t *) &tempMBR.partitions[1].lastSector);
393 } else {
394 tempMBR.partitions[1].partitionType = 0x00;
395 tempMBR.partitions[1].firstLBA = 0;
396 tempMBR.partitions[1].lengthLBA = 0;
397 moreLogicals = 0;
398 } // if/else
399 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
srs5694f2efa7d2011-03-01 22:03:26 -0500400 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
srs5694bf8950c2011-03-12 01:23:12 -0500401 partNum = next;
srs5694f2efa7d2011-03-01 22:03:26 -0500402 } // while
srs5694bf8950c2011-03-12 01:23:12 -0500403 DeleteExtendedParts();
srs5694f2efa7d2011-03-01 22:03:26 -0500404 return allOK;
405} // BasicMBRData::WriteMBRData(DiskIO *theDisk)
406
407int BasicMBRData::WriteMBRData(const string & deviceFilename) {
408 device = deviceFilename;
409 return WriteMBRData();
410} // BasicMBRData::WriteMBRData(const string & deviceFilename)
411
412// Write a single MBR record to the specified sector. Used by the like-named
413// function to write both the MBR and multiple EBR (for logical partition)
414// records.
415// Returns 1 on success, 0 on failure
416int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
417 int i, allOK;
418
419 // Reverse the byte order, if necessary
420 if (IsLittleEndian() == 0) {
421 ReverseBytes(&mbr.diskSignature, 4);
422 ReverseBytes(&mbr.nulls, 2);
423 ReverseBytes(&mbr.MBRSignature, 2);
424 for (i = 0; i < 4; i++) {
425 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
426 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
427 } // for
428 } // if
429
430 // Now write the data structure...
431 allOK = theDisk->OpenForWrite();
432 if (allOK && theDisk->Seek(sector)) {
433 if (theDisk->Write(&mbr, 512) != 512) {
434 allOK = 0;
435 cerr << "Error " << errno << " when saving MBR!\n";
436 } // if
437 } else {
438 allOK = 0;
439 cerr << "Error " << errno << " when seeking to MBR to write it!\n";
440 } // if/else
441 theDisk->Close();
442
443 // Reverse the byte order back, if necessary
444 if (IsLittleEndian() == 0) {
445 ReverseBytes(&mbr.diskSignature, 4);
446 ReverseBytes(&mbr.nulls, 2);
447 ReverseBytes(&mbr.MBRSignature, 2);
448 for (i = 0; i < 4; i++) {
449 ReverseBytes(&mbr.partitions[i].firstLBA, 4);
450 ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
451 } // for
452 }// if
453 return allOK;
454} // BasicMBRData::WriteMBRData(uint64_t sector)
455
srs5694bf8950c2011-03-12 01:23:12 -0500456// Set a new disk device; used in copying one disk's partition
457// table to another disk.
458void BasicMBRData::SetDisk(DiskIO *theDisk) {
459 int err;
460
461 myDisk = theDisk;
462 diskSize = theDisk->DiskSize(&err);
463 canDeleteMyDisk = 0;
464 ReadCHSGeom();
465} // BasicMBRData::SetDisk()
466
srs5694f2efa7d2011-03-01 22:03:26 -0500467/********************************************
468 * *
469 * Functions that display data for the user *
470 * *
471 ********************************************/
472
473// Show the MBR data to the user, up to the specified maximum number
474// of partitions....
srs5694bf8950c2011-03-12 01:23:12 -0500475void BasicMBRData::DisplayMBRData(void) {
srs5694f2efa7d2011-03-01 22:03:26 -0500476 int i;
srs5694f2efa7d2011-03-01 22:03:26 -0500477
srs5694bf8950c2011-03-12 01:23:12 -0500478 cout << "\nDisk size is " << diskSize << " sectors ("
srs569401f7f082011-03-15 23:53:31 -0400479 << BytesToIeee(diskSize, blockSize) << ")\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500480 cout << "MBR disk identifier: 0x";
481 cout.width(8);
482 cout.fill('0');
483 cout.setf(ios::uppercase);
484 cout << hex << diskSignature << dec << "\n";
srs5694bf8950c2011-03-12 01:23:12 -0500485 cout << "MBR partitions:\n\n";
486 if ((state == gpt) || (state == hybrid)) {
487 cout << "Number Boot Start Sector End Sector Status Code\n";
488 } else {
489 cout << " Can Be Can Be\n";
490 cout << "Number Boot Start Sector End Sector Status Logical Primary Code\n";
491 UpdateCanBeLogical();
492 } //
493 for (i = 0; i < MAX_MBR_PARTS; i++) {
494 if (partitions[i].GetLengthLBA() != 0) {
srs5694f2efa7d2011-03-01 22:03:26 -0500495 cout.fill(' ');
496 cout.width(4);
srs5694bf8950c2011-03-12 01:23:12 -0500497 cout << i + 1 << " ";
498 partitions[i].ShowData((state == gpt) || (state == hybrid));
srs5694f2efa7d2011-03-01 22:03:26 -0500499 } // if
500 cout.fill(' ');
501 } // for
srs5694f2efa7d2011-03-01 22:03:26 -0500502} // BasicMBRData::DisplayMBRData()
503
504// Displays the state, as a word, on stdout. Used for debugging & to
505// tell the user about the MBR state when the program launches....
506void BasicMBRData::ShowState(void) {
507 switch (state) {
508 case invalid:
509 cout << " MBR: not present\n";
510 break;
511 case gpt:
512 cout << " MBR: protective\n";
513 break;
514 case hybrid:
515 cout << " MBR: hybrid\n";
516 break;
517 case mbr:
518 cout << " MBR: MBR only\n";
519 break;
520 default:
521 cout << "\a MBR: unknown -- bug!\n";
522 break;
523 } // switch
524} // BasicMBRData::ShowState()
525
srs5694bf8950c2011-03-12 01:23:12 -0500526/************************
527 * *
528 * GPT Checks and fixes *
529 * *
530 ************************/
531
532// Perform a very rudimentary check for GPT data on the disk; searches for
533// the GPT signature in the main and backup metadata areas.
534// Returns 0 if GPT data not found, 1 if main data only is found, 2 if
535// backup only is found, 3 if both main and backup data are found, and
536// -1 if a disk error occurred.
537int BasicMBRData::CheckForGPT(void) {
538 int retval = 0, err;
539 char signature1[9], signature2[9];
540
541 if (myDisk != NULL) {
542 if (myDisk->OpenForRead() != 0) {
543 if (myDisk->Seek(1)) {
544 myDisk->Read(signature1, 8);
545 signature1[8] = '\0';
546 } else retval = -1;
547 if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) {
548 myDisk->Read(signature2, 8);
549 signature2[8] = '\0';
550 } else retval = -1;
551 if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0))
552 retval += 1;
553 if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0))
554 retval += 2;
555 } else {
556 retval = -1;
557 } // if/else
558 myDisk->Close();
559 } else retval = -1;
560 return retval;
561} // BasicMBRData::CheckForGPT()
562
563// Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk,
564// but only if GPT data are verified on the disk, and only for the sector(s)
565// with GPT signatures.
566// Returns 1 if operation completes successfully, 0 if not (returns 1 if
567// no GPT data are found on the disk).
568int BasicMBRData::BlankGPTData(void) {
569 int allOK = 1, err;
570 uint8_t blank[512];
571
572 memset(blank, 0, 512);
573 switch (CheckForGPT()) {
574 case -1:
575 allOK = 0;
576 break;
577 case 0:
578 break;
579 case 1:
580 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
581 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
582 allOK = 0;
583 myDisk->Close();
584 } else allOK = 0;
585 break;
586 case 2:
587 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
588 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
589 (myDisk->Write(blank, 512) == 512)))
590 allOK = 0;
591 myDisk->Close();
592 } else allOK = 0;
593 break;
594 case 3:
595 if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
596 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
597 allOK = 0;
598 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
599 (myDisk->Write(blank, 512) == 512)))
600 allOK = 0;
601 myDisk->Close();
602 } else allOK = 0;
603 break;
604 default:
605 break;
606 } // switch()
607 return allOK;
608} // BasicMBRData::BlankGPTData
609
srs5694f2efa7d2011-03-01 22:03:26 -0500610/*********************************************************************
611 * *
612 * Functions that set or get disk metadata (CHS geometry, disk size, *
613 * etc.) *
614 * *
615 *********************************************************************/
616
srs5694bf8950c2011-03-12 01:23:12 -0500617// Read the CHS geometry using OS calls, or if that fails, set to
618// the most common value for big disks (255 heads, 63 sectors per
619// track, & however many cylinders that computes to).
620void BasicMBRData::ReadCHSGeom(void) {
srs5694a17fe692011-09-10 20:30:20 -0400621 int err;
622
srs5694bf8950c2011-03-12 01:23:12 -0500623 numHeads = myDisk->GetNumHeads();
624 numSecspTrack = myDisk->GetNumSecsPerTrack();
srs5694a17fe692011-09-10 20:30:20 -0400625 diskSize = myDisk->DiskSize(&err);
626 blockSize = myDisk->GetBlockSize();
srs5694bf8950c2011-03-12 01:23:12 -0500627 partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize);
628} // BasicMBRData::ReadCHSGeom()
629
630// Find the low and high used partition numbers (numbered from 0).
631// Return value is the number of partitions found. Note that the
632// *low and *high values are both set to 0 when no partitions
633// are found, as well as when a single partition in the first
634// position exists. Thus, the return value is the only way to
635// tell when no partitions exist.
636int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) {
637 uint32_t i;
638 int numFound = 0;
639
640 *low = MAX_MBR_PARTS + 1; // code for "not found"
641 *high = 0;
642 for (i = 0; i < MAX_MBR_PARTS; i++) {
643 if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists
644 *high = i; // since we're counting up, set the high value
645 // Set the low value only if it's not yet found...
646 if (*low == (MAX_MBR_PARTS + 1))
647 *low = i;
648 numFound++;
649 } // if
650 } // for
651
652 // Above will leave *low pointing to its "not found" value if no partitions
653 // are defined, so reset to 0 if this is the case....
654 if (*low == (MAX_MBR_PARTS + 1))
655 *low = 0;
656 return numFound;
657} // GPTData::GetPartRange()
srs5694f2efa7d2011-03-01 22:03:26 -0500658
659// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
660// was within the range that can be expressed by CHS (including 0, for an
661// empty partition), 0 if the value is outside that range, and -1 if chs is
662// invalid.
663int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
664 uint64_t cylinder, head, sector; // all numbered from 0
665 uint64_t remainder;
666 int retval = 1;
667 int done = 0;
668
669 if (chs != NULL) {
670 // Special case: In case of 0 LBA value, zero out CHS values....
671 if (lba == 0) {
672 chs[0] = chs[1] = chs[2] = UINT8_C(0);
673 done = 1;
674 } // if
675 // If LBA value is too large for CHS, max out CHS values....
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500676 if ((!done) && (lba >= ((uint64_t) numHeads * numSecspTrack * MAX_CYLINDERS))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500677 chs[0] = 254;
678 chs[1] = chs[2] = 255;
679 done = 1;
680 retval = 0;
681 } // if
682 // If neither of the above applies, compute CHS values....
683 if (!done) {
684 cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
685 remainder = lba - (cylinder * numHeads * numSecspTrack);
686 head = remainder / numSecspTrack;
687 remainder -= head * numSecspTrack;
688 sector = remainder;
689 if (head < numHeads)
690 chs[0] = (uint8_t) head;
691 else
692 retval = 0;
693 if (sector < numSecspTrack) {
694 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
695 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
696 } else {
697 retval = 0;
698 } // if/else
699 } // if value is expressible and non-0
700 } else { // Invalid (NULL) chs pointer
701 retval = -1;
702 } // if CHS pointer valid
703 return (retval);
704} // BasicMBRData::LBAtoCHS()
705
Roderick W. Smith427c7992013-09-12 12:37:44 -0400706// Look for overlapping partitions. Also looks for a couple of non-error
707// conditions that the user should be told about.
srs5694bf8950c2011-03-12 01:23:12 -0500708// Returns the number of problems found
709int BasicMBRData::FindOverlaps(void) {
Roderick W. Smith427c7992013-09-12 12:37:44 -0400710 int i, j, numProbs = 0, numEE = 0, ProtectiveOnOne = 0;
srs5694f2efa7d2011-03-01 22:03:26 -0500711
712 for (i = 0; i < MAX_MBR_PARTS; i++) {
713 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
srs569423d8d542011-10-01 18:40:10 -0400714 if ((partitions[i].GetInclusion() != NONE) && (partitions[j].GetInclusion() != NONE) &&
srs5694bf8950c2011-03-12 01:23:12 -0500715 (partitions[i].DoTheyOverlap(partitions[j]))) {
srs5694f2efa7d2011-03-01 22:03:26 -0500716 numProbs++;
717 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
718 << " overlap!\n";
719 } // if
720 } // for (j...)
srs5694bf8950c2011-03-12 01:23:12 -0500721 if (partitions[i].GetType() == 0xEE) {
srs5694f2efa7d2011-03-01 22:03:26 -0500722 numEE++;
Roderick W. Smith427c7992013-09-12 12:37:44 -0400723 if (partitions[i].GetStartLBA() == 1)
724 ProtectiveOnOne = 1;
srs5694f2efa7d2011-03-01 22:03:26 -0500725 } // if
726 } // for (i...)
Roderick W. Smith427c7992013-09-12 12:37:44 -0400727
srs5694f2efa7d2011-03-01 22:03:26 -0500728 if (numEE > 1)
729 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
730 << "in some OSes.\n";
Roderick W. Smith22e88b52014-02-17 12:07:02 -0500731 if (!ProtectiveOnOne && (numEE > 0))
Roderick W. Smith427c7992013-09-12 12:37:44 -0400732 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause "
733 << "problems\nin some OSes.\n";
srs5694f2efa7d2011-03-01 22:03:26 -0500734
735 return numProbs;
srs5694bf8950c2011-03-12 01:23:12 -0500736} // BasicMBRData::FindOverlaps()
737
738// Returns the number of primary partitions, including the extended partition
739// required to hold any logical partitions found.
740int BasicMBRData::NumPrimaries(void) {
741 int i, numPrimaries = 0, logicalsFound = 0;
742
743 for (i = 0; i < MAX_MBR_PARTS; i++) {
744 if (partitions[i].GetLengthLBA() > 0) {
745 if (partitions[i].GetInclusion() == PRIMARY)
746 numPrimaries++;
747 if (partitions[i].GetInclusion() == LOGICAL)
748 logicalsFound = 1;
749 } // if
750 } // for
751 return (numPrimaries + logicalsFound);
752} // BasicMBRData::NumPrimaries()
753
754// Returns the number of logical partitions.
755int BasicMBRData::NumLogicals(void) {
756 int i, numLogicals = 0;
757
758 for (i = 0; i < MAX_MBR_PARTS; i++) {
759 if (partitions[i].GetInclusion() == LOGICAL)
760 numLogicals++;
761 } // for
762 return numLogicals;
763} // BasicMBRData::NumLogicals()
764
765// Returns the number of partitions (primaries plus logicals), NOT including
766// the extended partition required to house the logicals.
767int BasicMBRData::CountParts(void) {
768 int i, num = 0;
769
770 for (i = 0; i < MAX_MBR_PARTS; i++) {
771 if ((partitions[i].GetInclusion() == LOGICAL) ||
772 (partitions[i].GetInclusion() == PRIMARY))
773 num++;
774 } // for
775 return num;
776} // BasicMBRData::CountParts()
777
778// Updates the canBeLogical and canBePrimary flags for all the partitions.
779void BasicMBRData::UpdateCanBeLogical(void) {
780 int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR;
781 uint64_t firstLogical, lastLogical, lStart, pStart;
782
783 numPrimaries = NumPrimaries();
784 numLogicals = NumLogicals();
785 firstLogical = FirstLogicalLBA() - 1;
786 lastLogical = LastLogicalLBA();
787 for (i = 0; i < MAX_MBR_PARTS; i++) {
788 usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR);
789 if (usedAsEBR) {
790 partitions[i].SetCanBeLogical(0);
791 partitions[i].SetCanBePrimary(0);
792 } else if (partitions[i].GetLengthLBA() > 0) {
793 // First determine if it can be logical....
794 sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1);
795 lStart = partitions[i].GetStartLBA(); // start of potential logical part.
796 if ((lastLogical > 0) &&
797 ((sectorBefore == EBR) || (sectorBefore == NONE))) {
798 // Assume it can be logical, then search for primaries that make it
799 // not work and, if found, flag appropriately.
800 partitions[i].SetCanBeLogical(1);
801 for (j = 0; j < MAX_MBR_PARTS; j++) {
802 if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) {
803 pStart = partitions[j].GetStartLBA();
804 if (((pStart < lStart) && (firstLogical < pStart)) ||
805 ((pStart > lStart) && (firstLogical > pStart))) {
806 partitions[i].SetCanBeLogical(0);
807 } // if/else
808 } // if
809 } // for
810 } else {
811 if ((sectorBefore != EBR) && (sectorBefore != NONE))
812 partitions[i].SetCanBeLogical(0);
813 else
814 partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already
815 } // if/else
816 // Now determine if it can be primary. Start by assuming it can be...
817 partitions[i].SetCanBePrimary(1);
818 if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) {
819 partitions[i].SetCanBePrimary(0);
820 if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) &&
821 (numPrimaries == 4))
822 partitions[i].SetCanBePrimary(1);
823 } // if
824 if ((partitions[i].GetStartLBA() > (firstLogical + 1)) &&
825 (partitions[i].GetLastLBA() < lastLogical))
826 partitions[i].SetCanBePrimary(0);
827 } // else if
828 } // for
829} // BasicMBRData::UpdateCanBeLogical()
830
831// Returns the first sector occupied by any logical partition. Note that
832// this does NOT include the logical partition's EBR! Returns UINT32_MAX
833// if there are no logical partitions defined.
834uint64_t BasicMBRData::FirstLogicalLBA(void) {
835 int i;
836 uint64_t firstFound = UINT32_MAX;
837
838 for (i = 0; i < MAX_MBR_PARTS; i++) {
839 if ((partitions[i].GetInclusion() == LOGICAL) &&
840 (partitions[i].GetStartLBA() < firstFound)) {
841 firstFound = partitions[i].GetStartLBA();
842 } // if
843 } // for
844 return firstFound;
845} // BasicMBRData::FirstLogicalLBA()
846
847// Returns the last sector occupied by any logical partition, or 0 if
848// there are no logical partitions defined.
849uint64_t BasicMBRData::LastLogicalLBA(void) {
850 int i;
851 uint64_t lastFound = 0;
852
853 for (i = 0; i < MAX_MBR_PARTS; i++) {
854 if ((partitions[i].GetInclusion() == LOGICAL) &&
855 (partitions[i].GetLastLBA() > lastFound))
856 lastFound = partitions[i].GetLastLBA();
857 } // for
858 return lastFound;
859} // BasicMBRData::LastLogicalLBA()
860
861// Returns 1 if logical partitions are contiguous (have no primaries
862// in their midst), or 0 if one or more primaries exist between
863// logicals.
864int BasicMBRData::AreLogicalsContiguous(void) {
865 int allOK = 1, i = 0;
866 uint64_t firstLogical, lastLogical;
867
868 firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR
869 lastLogical = LastLogicalLBA();
870 if (lastLogical > 0) {
871 do {
872 if ((partitions[i].GetInclusion() == PRIMARY) &&
873 (partitions[i].GetStartLBA() >= firstLogical) &&
874 (partitions[i].GetStartLBA() <= lastLogical)) {
875 allOK = 0;
876 } // if
877 i++;
878 } while ((i < MAX_MBR_PARTS) && allOK);
879 } // if
880 return allOK;
881} // BasicMBRData::AreLogicalsContiguous()
882
883// Returns 1 if all partitions fit on the disk, given its size; 0 if any
884// partition is too big.
885int BasicMBRData::DoTheyFit(void) {
886 int i, allOK = 1;
887
888 for (i = 0; i < MAX_MBR_PARTS; i++) {
889 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) {
890 allOK = 0;
891 } // if
892 } // for
893 return allOK;
894} // BasicMBRData::DoTheyFit(void)
895
896// Returns 1 if there's at least one free sector immediately preceding
897// all partitions flagged as logical; 0 if any logical partition lacks
898// this space.
899int BasicMBRData::SpaceBeforeAllLogicals(void) {
900 int i = 0, allOK = 1;
901
902 do {
903 if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) {
904 allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR);
srs5694bf8950c2011-03-12 01:23:12 -0500905 } // if
906 i++;
907 } while (allOK && (i < MAX_MBR_PARTS));
908 return allOK;
909} // BasicMBRData::SpaceBeforeAllLogicals()
910
911// Returns 1 if the partitions describe a legal layout -- all logicals
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400912// are contiguous and have at least one preceding empty sector,
srs5694bf8950c2011-03-12 01:23:12 -0500913// the number of primaries is under 4 (or under 3 if there are any
914// logicals), there are no overlapping partitions, etc.
915// Does NOT assume that primaries are numbered 1-4; uses the
916// IsItPrimary() function of the MBRPart class to determine
917// primary status. Also does NOT consider partition order; there
918// can be gaps and it will still be considered legal.
919int BasicMBRData::IsLegal(void) {
920 int allOK = 1;
921
922 allOK = (FindOverlaps() == 0);
923 allOK = (allOK && (NumPrimaries() <= 4));
924 allOK = (allOK && AreLogicalsContiguous());
925 allOK = (allOK && DoTheyFit());
926 allOK = (allOK && SpaceBeforeAllLogicals());
927 return allOK;
928} // BasicMBRData::IsLegal()
929
Roderick W. Smith042f38a2013-08-31 17:40:15 -0400930// Returns 1 if the 0xEE partition in the protective/hybrid MBR is marked as
931// active/bootable.
932int BasicMBRData::IsEEActive(void) {
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400933 int i, IsActive = 0;
Roderick W. Smith042f38a2013-08-31 17:40:15 -0400934
935 for (i = 0; i < MAX_MBR_PARTS; i++) {
Roderick W. Smith427c7992013-09-12 12:37:44 -0400936 if ((partitions[i].GetStatus() & 0x80) && (partitions[i].GetType() == 0xEE))
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400937 IsActive = 1;
Roderick W. Smith042f38a2013-08-31 17:40:15 -0400938 }
939 return IsActive;
940} // BasicMBRData::IsEEActive()
941
srs5694bf8950c2011-03-12 01:23:12 -0500942// Finds the next in-use partition, starting with start (will return start
943// if it's in use). Returns -1 if no subsequent partition is in use.
944int BasicMBRData::FindNextInUse(int start) {
945 if (start >= MAX_MBR_PARTS)
946 start = -1;
947 while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE))
948 start++;
949 if ((start < 0) || (start >= MAX_MBR_PARTS))
950 start = -1;
951 return start;
952} // BasicMBRData::FindFirstLogical();
srs5694f2efa7d2011-03-01 22:03:26 -0500953
954/*****************************************************
955 * *
956 * Functions to create, delete, or change partitions *
957 * *
958 *****************************************************/
959
960// Empty all data. Meant mainly for calling by constructors, but it's also
961// used by the hybrid MBR functions in the GPTData class.
962void BasicMBRData::EmptyMBR(int clearBootloader) {
963 int i;
964
965 // Zero out the boot loader section, the disk signature, and the
966 // 2-byte nulls area only if requested to do so. (This is the
967 // default.)
968 if (clearBootloader == 1) {
969 EmptyBootloader();
970 } // if
971
972 // Blank out the partitions
973 for (i = 0; i < MAX_MBR_PARTS; i++) {
srs5694bf8950c2011-03-12 01:23:12 -0500974 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -0500975 } // for
976 MBRSignature = MBR_SIGNATURE;
srs5694bf8950c2011-03-12 01:23:12 -0500977 state = mbr;
srs5694f2efa7d2011-03-01 22:03:26 -0500978} // BasicMBRData::EmptyMBR()
979
980// Blank out the boot loader area. Done with the initial MBR-to-GPT
981// conversion, since MBR boot loaders don't understand GPT, and so
982// need to be replaced....
983void BasicMBRData::EmptyBootloader(void) {
984 int i;
985
986 for (i = 0; i < 440; i++)
987 code[i] = 0;
988 nulls = 0;
989} // BasicMBRData::EmptyBootloader
990
srs5694bf8950c2011-03-12 01:23:12 -0500991// Create a partition of the specified number based on the passed
992// partition. This function does *NO* error checking, so it's possible
srs5694f2efa7d2011-03-01 22:03:26 -0500993// to seriously screw up a partition table using this function!
994// Note: This function should NOT be used to create the 0xEE partition
995// in a conventional GPT configuration, since that partition has
996// specific size requirements that this function won't handle. It may
997// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
998// since those toss the rulebook away anyhow....
srs5694bf8950c2011-03-12 01:23:12 -0500999void BasicMBRData::AddPart(int num, const MBRPart& newPart) {
1000 partitions[num] = newPart;
1001} // BasicMBRData::AddPart()
1002
1003// Create a partition of the specified number, starting LBA, and
1004// length. This function does almost no error checking, so it's possible
1005// to seriously screw up a partition table using this function!
1006// Note: This function should NOT be used to create the 0xEE partition
1007// in a conventional GPT configuration, since that partition has
1008// specific size requirements that this function won't handle. It may
1009// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
1010// since those toss the rulebook away anyhow....
1011void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) {
1012 if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) {
1013 partitions[num].Empty();
1014 partitions[num].SetType(type);
1015 partitions[num].SetLocation(start, length);
1016 if (num < 4)
1017 partitions[num].SetInclusion(PRIMARY);
1018 else
1019 partitions[num].SetInclusion(LOGICAL);
srs5694f2efa7d2011-03-01 22:03:26 -05001020 SetPartBootable(num, bootable);
srs5694bf8950c2011-03-12 01:23:12 -05001021 } // if valid partition number & size
srs5694f2efa7d2011-03-01 22:03:26 -05001022} // BasicMBRData::MakePart()
1023
1024// Set the partition's type code.
1025// Returns 1 if successful, 0 if not (invalid partition number)
1026int BasicMBRData::SetPartType(int num, int type) {
1027 int allOK = 1;
1028
1029 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001030 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
1031 allOK = partitions[num].SetType(type);
srs5694f2efa7d2011-03-01 22:03:26 -05001032 } else allOK = 0;
1033 } else allOK = 0;
1034 return allOK;
1035} // BasicMBRData::SetPartType()
1036
1037// Set (or remove) the partition's bootable flag. Setting it is the
1038// default; pass 0 as bootable to remove the flag.
1039// Returns 1 if successful, 0 if not (invalid partition number)
1040int BasicMBRData::SetPartBootable(int num, int bootable) {
1041 int allOK = 1;
1042
1043 if ((num >= 0) && (num < MAX_MBR_PARTS)) {
srs5694bf8950c2011-03-12 01:23:12 -05001044 if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001045 if (bootable == 0)
srs5694bf8950c2011-03-12 01:23:12 -05001046 partitions[num].SetStatus(UINT8_C(0x00));
srs5694f2efa7d2011-03-01 22:03:26 -05001047 else
srs5694bf8950c2011-03-12 01:23:12 -05001048 partitions[num].SetStatus(UINT8_C(0x80));
srs5694f2efa7d2011-03-01 22:03:26 -05001049 } else allOK = 0;
1050 } else allOK = 0;
1051 return allOK;
1052} // BasicMBRData::SetPartBootable()
1053
1054// Create a partition that fills the most available space. Returns
1055// 1 if partition was created, 0 otherwise. Intended for use in
1056// creating hybrid MBRs.
1057int BasicMBRData::MakeBiggestPart(int i, int type) {
srs5694bf8950c2011-03-12 01:23:12 -05001058 uint64_t start = UINT64_C(1); // starting point for each search
1059 uint64_t firstBlock; // first block in a segment
1060 uint64_t lastBlock; // last block in a segment
1061 uint64_t segmentSize; // size of segment in blocks
1062 uint64_t selectedSegment = UINT64_C(0); // location of largest segment
1063 uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks
srs5694f2efa7d2011-03-01 22:03:26 -05001064 int found = 0;
Roderick W. Smith427c7992013-09-12 12:37:44 -04001065 string anything;
srs5694f2efa7d2011-03-01 22:03:26 -05001066
1067 do {
1068 firstBlock = FindFirstAvailable(start);
srs5694bf8950c2011-03-12 01:23:12 -05001069 if (firstBlock > UINT64_C(0)) { // something's free...
srs5694f2efa7d2011-03-01 22:03:26 -05001070 lastBlock = FindLastInFree(firstBlock);
srs5694bf8950c2011-03-12 01:23:12 -05001071 segmentSize = lastBlock - firstBlock + UINT64_C(1);
srs5694f2efa7d2011-03-01 22:03:26 -05001072 if (segmentSize > selectedSize) {
1073 selectedSize = segmentSize;
1074 selectedSegment = firstBlock;
1075 } // if
1076 start = lastBlock + 1;
1077 } // if
1078 } while (firstBlock != 0);
srs5694bf8950c2011-03-12 01:23:12 -05001079 if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) {
srs5694f2efa7d2011-03-01 22:03:26 -05001080 found = 1;
1081 MakePart(i, selectedSegment, selectedSize, type, 0);
1082 } else {
1083 found = 0;
1084 } // if/else
1085 return found;
1086} // BasicMBRData::MakeBiggestPart(int i)
1087
1088// Delete partition #i
1089void BasicMBRData::DeletePartition(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001090 partitions[i].Empty();
srs5694f2efa7d2011-03-01 22:03:26 -05001091} // BasicMBRData::DeletePartition()
1092
srs5694bf8950c2011-03-12 01:23:12 -05001093// Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity
1094// checks to ensure the table remains legal.
1095// Returns 1 on success, 0 on failure.
1096int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) {
1097 int allOK = 1, origValue;
1098
1099 if (IsLegal()) {
1100 if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) {
1101 origValue = partitions[num].GetInclusion();
1102 partitions[num].SetInclusion(inclStatus);
1103 if (!IsLegal()) {
1104 partitions[num].SetInclusion(origValue);
1105 cerr << "Specified change is not legal! Aborting change!\n";
1106 } // if
1107 } else {
1108 cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n";
1109 } // if/else
1110 } else {
1111 cerr << "Partition table is not currently in a valid state. Aborting change!\n";
1112 allOK = 0;
1113 } // if/else
1114 return allOK;
1115} // BasicMBRData::SetInclusionwChecks()
1116
srs5694f2efa7d2011-03-01 22:03:26 -05001117// Recomputes the CHS values for the specified partition and adjusts the value.
1118// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
1119// protective partitions, but this is required by some buggy BIOSes, so I'm
1120// providing a function to do this deliberately at the user's command.
1121// This function does nothing if the partition's length is 0.
1122void BasicMBRData::RecomputeCHS(int partNum) {
srs5694bf8950c2011-03-12 01:23:12 -05001123 partitions[partNum].RecomputeCHS();
srs5694f2efa7d2011-03-01 22:03:26 -05001124} // BasicMBRData::RecomputeCHS()
1125
srs5694699941e2011-03-21 21:33:57 -04001126// Sorts the partitions starting with partition #start. This function
1127// does NOT pay attention to primary/logical assignment, which is
1128// critical when writing the partitions.
srs5694bf8950c2011-03-12 01:23:12 -05001129void BasicMBRData::SortMBR(int start) {
srs5694c2f6e0c2011-03-16 02:42:33 -04001130 if ((start < MAX_MBR_PARTS) && (start >= 0))
1131 sort(partitions + start, partitions + MAX_MBR_PARTS);
1132} // BasicMBRData::SortMBR()
srs5694bf8950c2011-03-12 01:23:12 -05001133
1134// Delete any partitions that are too big to fit on the disk
1135// or that are too big for MBR (32-bit limits).
srs569400b6d7a2011-06-26 22:40:06 -04001136// This deletes the partitions by setting values to 0, not just
1137// by setting them as being omitted.
srs5694bf8950c2011-03-12 01:23:12 -05001138// Returns the number of partitions deleted in this way.
1139int BasicMBRData::DeleteOversizedParts() {
1140 int num = 0, i;
1141
1142 for (i = 0; i < MAX_MBR_PARTS; i++) {
1143 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) ||
1144 (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) {
srs569400b6d7a2011-06-26 22:40:06 -04001145 cerr << "\aWarning: Deleting oversized partition #" << i + 1 << "! Start = "
1146 << partitions[i].GetStartLBA() << ", length = " << partitions[i].GetLengthLBA() << "\n";
srs5694bf8950c2011-03-12 01:23:12 -05001147 partitions[i].Empty();
1148 num++;
1149 } // if
1150 } // for
1151 return num;
1152} // BasicMBRData::DeleteOversizedParts()
1153
1154// Search for and delete extended partitions.
1155// Returns the number of partitions deleted.
1156int BasicMBRData::DeleteExtendedParts() {
1157 int i, numDeleted = 0;
1158 uint8_t type;
1159
1160 for (i = 0; i < MAX_MBR_PARTS; i++) {
1161 type = partitions[i].GetType();
1162 if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) &&
1163 (partitions[i].GetLengthLBA() > 0)) {
1164 partitions[i].Empty();
1165 numDeleted++;
1166 } // if
1167 } // for
1168 return numDeleted;
1169} // BasicMBRData::DeleteExtendedParts()
1170
1171// Finds any overlapping partitions and omits the smaller of the two.
1172void BasicMBRData::OmitOverlaps() {
1173 int i, j;
1174
1175 for (i = 0; i < MAX_MBR_PARTS; i++) {
1176 for (j = i + 1; j < MAX_MBR_PARTS; j++) {
1177 if ((partitions[i].GetInclusion() != NONE) &&
1178 partitions[i].DoTheyOverlap(partitions[j])) {
1179 if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA())
1180 partitions[i].SetInclusion(NONE);
1181 else
1182 partitions[j].SetInclusion(NONE);
srs5694f2efa7d2011-03-01 22:03:26 -05001183 } // if
srs5694bf8950c2011-03-12 01:23:12 -05001184 } // for (j...)
1185 } // for (i...)
1186} // BasicMBRData::OmitOverlaps()
1187
srs5694bf8950c2011-03-12 01:23:12 -05001188// Convert as many partitions into logicals as possible, except for
1189// the first partition, if possible.
1190void BasicMBRData::MaximizeLogicals() {
1191 int earliestPart = 0, earliestPartWas = NONE, i;
1192
1193 for (i = MAX_MBR_PARTS - 1; i >= 0; i--) {
1194 UpdateCanBeLogical();
1195 earliestPart = i;
1196 if (partitions[i].CanBeLogical()) {
1197 partitions[i].SetInclusion(LOGICAL);
1198 } else if (partitions[i].CanBePrimary()) {
1199 partitions[i].SetInclusion(PRIMARY);
1200 } else {
1201 partitions[i].SetInclusion(NONE);
1202 } // if/elseif/else
1203 } // for
1204 // If we have spare primaries, convert back the earliest partition to
1205 // its original state....
1206 if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL))
1207 partitions[earliestPart].SetInclusion(earliestPartWas);
1208} // BasicMBRData::MaximizeLogicals()
1209
1210// Add primaries up to the maximum allowed, from the omitted category.
1211void BasicMBRData::MaximizePrimaries() {
1212 int num, i = 0;
1213
1214 num = NumPrimaries();
1215 while ((num < 4) && (i < MAX_MBR_PARTS)) {
1216 if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) {
1217 partitions[i].SetInclusion(PRIMARY);
srs5694699941e2011-03-21 21:33:57 -04001218 num++;
1219 UpdateCanBeLogical();
srs5694bf8950c2011-03-12 01:23:12 -05001220 } // if
1221 i++;
1222 } // while
1223} // BasicMBRData::MaximizePrimaries()
1224
1225// Remove primary partitions in excess of 4, starting with the later ones,
1226// in terms of the array location....
1227void BasicMBRData::TrimPrimaries(void) {
Roderick W. Smith84aaff62014-02-17 16:17:11 -05001228 int numToDelete, i = MAX_MBR_PARTS - 1;
srs5694bf8950c2011-03-12 01:23:12 -05001229
1230 numToDelete = NumPrimaries() - 4;
1231 while ((numToDelete > 0) && (i >= 0)) {
1232 if (partitions[i].GetInclusion() == PRIMARY) {
1233 partitions[i].SetInclusion(NONE);
1234 numToDelete--;
1235 } // if
1236 i--;
1237 } // while (numToDelete > 0)
1238} // BasicMBRData::TrimPrimaries()
1239
1240// Locates primary partitions located between logical partitions and
1241// either converts the primaries into logicals (if possible) or omits
1242// them.
1243void BasicMBRData::MakeLogicalsContiguous(void) {
1244 uint64_t firstLogicalLBA, lastLogicalLBA;
1245 int i;
1246
1247 firstLogicalLBA = FirstLogicalLBA();
1248 lastLogicalLBA = LastLogicalLBA();
1249 for (i = 0; i < MAX_MBR_PARTS; i++) {
1250 if ((partitions[i].GetInclusion() == PRIMARY) &&
1251 (partitions[i].GetStartLBA() >= firstLogicalLBA) &&
1252 (partitions[i].GetLastLBA() <= lastLogicalLBA)) {
1253 if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE)
1254 partitions[i].SetInclusion(LOGICAL);
1255 else
1256 partitions[i].SetInclusion(NONE);
1257 } // if
1258 } // for
1259} // BasicMBRData::MakeLogicalsContiguous()
1260
1261// If MBR data aren't legal, adjust primary/logical assignments and,
1262// if necessary, drop partitions, to make the data legal.
1263void BasicMBRData::MakeItLegal(void) {
1264 if (!IsLegal()) {
1265 DeleteOversizedParts();
srs5694bf8950c2011-03-12 01:23:12 -05001266 MaximizeLogicals();
1267 MaximizePrimaries();
1268 if (!AreLogicalsContiguous())
1269 MakeLogicalsContiguous();
1270 if (NumPrimaries() > 4)
1271 TrimPrimaries();
1272 OmitOverlaps();
1273 } // if
1274} // BasicMBRData::MakeItLegal()
1275
1276// Removes logical partitions and deactivated partitions from first four
1277// entries (primary space).
1278// Returns the number of partitions moved.
1279int BasicMBRData::RemoveLogicalsFromFirstFour(void) {
1280 int i, j = 4, numMoved = 0, swapped = 0;
1281 MBRPart temp;
1282
1283 for (i = 0; i < 4; i++) {
1284 if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) {
1285 j = 4;
1286 swapped = 0;
1287 do {
1288 if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) {
1289 temp = partitions[j];
1290 partitions[j] = partitions[i];
1291 partitions[i] = temp;
1292 swapped = 1;
1293 numMoved++;
1294 } // if
1295 j++;
1296 } while ((j < MAX_MBR_PARTS) && !swapped);
1297 if (j >= MAX_MBR_PARTS)
1298 cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n";
1299 } // if
1300 } // for i...
1301 return numMoved;
1302} // BasicMBRData::RemoveLogicalsFromFirstFour()
1303
1304// Move all primaries into the first four partition spaces
1305// Returns the number of partitions moved.
1306int BasicMBRData::MovePrimariesToFirstFour(void) {
1307 int i, j = 0, numMoved = 0, swapped = 0;
1308 MBRPart temp;
1309
1310 for (i = 4; i < MAX_MBR_PARTS; i++) {
1311 if (partitions[i].GetInclusion() == PRIMARY) {
1312 j = 0;
1313 swapped = 0;
1314 do {
1315 if (partitions[j].GetInclusion() != PRIMARY) {
1316 temp = partitions[j];
1317 partitions[j] = partitions[i];
1318 partitions[i] = temp;
1319 swapped = 1;
1320 numMoved++;
1321 } // if
1322 j++;
1323 } while ((j < 4) && !swapped);
1324 } // if
1325 } // for
1326 return numMoved;
1327} // BasicMBRData::MovePrimariesToFirstFour()
1328
1329// Create an extended partition, if necessary, to hold the logical partitions.
1330// This function also sorts the primaries into the first four positions of
1331// the table.
1332// Returns 1 on success, 0 on failure.
1333int BasicMBRData::CreateExtended(void) {
1334 int allOK = 1, i = 0, swapped = 0;
1335 MBRPart temp;
1336
1337 if (IsLegal()) {
1338 // Move logicals out of primary space...
1339 RemoveLogicalsFromFirstFour();
1340 // Move primaries out of logical space...
1341 MovePrimariesToFirstFour();
1342
1343 // Create the extended partition
1344 if (NumLogicals() > 0) {
1345 SortMBR(4); // sort starting from 4 -- that is, logicals only
1346 temp.Empty();
1347 temp.SetStartLBA(FirstLogicalLBA() - 1);
1348 temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2);
1349 temp.SetType(0x0f, 1);
1350 temp.SetInclusion(PRIMARY);
1351 do {
1352 if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) {
1353 partitions[i] = temp;
1354 swapped = 1;
1355 } // if
1356 i++;
1357 } while ((i < 4) && !swapped);
1358 if (!swapped) {
1359 cerr << "Could not create extended partition; no room in primary table!\n";
1360 allOK = 0;
1361 } // if
1362 } // if (NumLogicals() > 0)
1363 } else allOK = 0;
1364 // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found
1365 // along with an extended partition
1366 for (i = 0; i < MAX_MBR_PARTS; i++)
1367 if (swapped && partitions[i].GetType() == 0xEE)
1368 allOK = 0;
1369 return allOK;
1370} // BasicMBRData::CreateExtended()
srs5694f2efa7d2011-03-01 22:03:26 -05001371
1372/****************************************
1373 * *
1374 * Functions to find data on free space *
1375 * *
1376 ****************************************/
1377
1378// Finds the first free space on the disk from start onward; returns 0
1379// if none available....
srs5694bf8950c2011-03-12 01:23:12 -05001380uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) {
1381 uint64_t first;
1382 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001383 int firstMoved;
1384
Roderick W. Smith427c7992013-09-12 12:37:44 -04001385 if ((start >= (UINT32_MAX - 1)) || (start >= (diskSize - 1)))
1386 return 0;
1387
srs5694f2efa7d2011-03-01 22:03:26 -05001388 first = start;
1389
1390 // ...now search through all partitions; if first is within an
1391 // existing partition, move it to the next sector after that
1392 // partition and repeat. If first was moved, set firstMoved
1393 // flag; repeat until firstMoved is not set, so as to catch
1394 // cases where partitions are out of sequential order....
1395 do {
1396 firstMoved = 0;
1397 for (i = 0; i < 4; i++) {
1398 // Check if it's in the existing partition
srs5694bf8950c2011-03-12 01:23:12 -05001399 if ((first >= partitions[i].GetStartLBA()) &&
1400 (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) {
1401 first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001402 firstMoved = 1;
1403 } // if
1404 } // for
1405 } while (firstMoved == 1);
srs5694bf8950c2011-03-12 01:23:12 -05001406 if ((first >= diskSize) || (first > UINT32_MAX))
srs5694f2efa7d2011-03-01 22:03:26 -05001407 first = 0;
1408 return (first);
1409} // BasicMBRData::FindFirstAvailable()
1410
1411// Finds the last free sector on the disk from start forward.
srs5694bf8950c2011-03-12 01:23:12 -05001412uint64_t BasicMBRData::FindLastInFree(uint64_t start) {
1413 uint64_t nearestStart;
1414 uint64_t i;
srs5694f2efa7d2011-03-01 22:03:26 -05001415
1416 if ((diskSize <= UINT32_MAX) && (diskSize > 0))
srs5694bf8950c2011-03-12 01:23:12 -05001417 nearestStart = diskSize - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001418 else
1419 nearestStart = UINT32_MAX - 1;
srs5694699941e2011-03-21 21:33:57 -04001420
srs5694f2efa7d2011-03-01 22:03:26 -05001421 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001422 if ((nearestStart > partitions[i].GetStartLBA()) &&
1423 (partitions[i].GetStartLBA() > start)) {
1424 nearestStart = partitions[i].GetStartLBA() - 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001425 } // if
1426 } // for
1427 return (nearestStart);
1428} // BasicMBRData::FindLastInFree()
1429
1430// Finds the first free sector on the disk from start backward.
srs5694bf8950c2011-03-12 01:23:12 -05001431uint64_t BasicMBRData::FindFirstInFree(uint64_t start) {
1432 uint64_t bestLastLBA, thisLastLBA;
srs5694f2efa7d2011-03-01 22:03:26 -05001433 int i;
1434
1435 bestLastLBA = 1;
1436 for (i = 0; i < 4; i++) {
srs5694bf8950c2011-03-12 01:23:12 -05001437 thisLastLBA = partitions[i].GetLastLBA() + 1;
srs5694f2efa7d2011-03-01 22:03:26 -05001438 if (thisLastLBA > 0)
1439 thisLastLBA--;
1440 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
1441 bestLastLBA = thisLastLBA + 1;
1442 } // for
1443 return (bestLastLBA);
1444} // BasicMBRData::FindFirstInFree()
1445
srs569423d8d542011-10-01 18:40:10 -04001446// Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID.
1447// Note: If the sector immediately before a logical partition is in use by
1448// another partition, this function returns PRIMARY or LOGICAL for that
1449// sector, rather than EBR.
srs5694bf8950c2011-03-12 01:23:12 -05001450int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) {
1451 int i = 0, usedAs = NONE;
srs5694f2efa7d2011-03-01 22:03:26 -05001452
srs5694bf8950c2011-03-12 01:23:12 -05001453 do {
1454 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector))
1455 usedAs = partitions[i].GetInclusion();
1456 if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL))
1457 usedAs = EBR;
1458 if (sector == 0)
1459 usedAs = EBR;
1460 if (sector >= diskSize)
1461 usedAs = INVALID;
1462 i++;
srs569423d8d542011-10-01 18:40:10 -04001463 } while ((i < topPartNum) && ((usedAs == NONE) || (usedAs == EBR)));
srs5694bf8950c2011-03-12 01:23:12 -05001464 return usedAs;
1465} // BasicMBRData::SectorUsedAs()
1466
srs5694f2efa7d2011-03-01 22:03:26 -05001467/******************************************************
1468 * *
1469 * Functions that extract data on specific partitions *
1470 * *
1471 ******************************************************/
1472
1473uint8_t BasicMBRData::GetStatus(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001474 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001475 uint8_t retval;
1476
1477 thePart = GetPartition(i);
1478 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001479 retval = thePart->GetStatus();
srs5694f2efa7d2011-03-01 22:03:26 -05001480 else
1481 retval = UINT8_C(0);
1482 return retval;
1483} // BasicMBRData::GetStatus()
1484
1485uint8_t BasicMBRData::GetType(int i) {
srs5694bf8950c2011-03-12 01:23:12 -05001486 MBRPart* thePart;
srs5694f2efa7d2011-03-01 22:03:26 -05001487 uint8_t retval;
1488
1489 thePart = GetPartition(i);
1490 if (thePart != NULL)
srs5694bf8950c2011-03-12 01:23:12 -05001491 retval = thePart->GetType();
srs5694f2efa7d2011-03-01 22:03:26 -05001492 else
1493 retval = UINT8_C(0);
1494 return retval;
1495} // BasicMBRData::GetType()
1496
srs5694bf8950c2011-03-12 01:23:12 -05001497uint64_t BasicMBRData::GetFirstSector(int i) {
1498 MBRPart* thePart;
1499 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001500
1501 thePart = GetPartition(i);
1502 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001503 retval = thePart->GetStartLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001504 } else
1505 retval = UINT32_C(0);
1506 return retval;
1507} // BasicMBRData::GetFirstSector()
1508
srs5694bf8950c2011-03-12 01:23:12 -05001509uint64_t BasicMBRData::GetLength(int i) {
1510 MBRPart* thePart;
1511 uint64_t retval;
srs5694f2efa7d2011-03-01 22:03:26 -05001512
1513 thePart = GetPartition(i);
1514 if (thePart != NULL) {
srs5694bf8950c2011-03-12 01:23:12 -05001515 retval = thePart->GetLengthLBA();
srs5694f2efa7d2011-03-01 22:03:26 -05001516 } else
srs5694bf8950c2011-03-12 01:23:12 -05001517 retval = UINT64_C(0);
srs5694f2efa7d2011-03-01 22:03:26 -05001518 return retval;
1519} // BasicMBRData::GetLength()
1520
1521/***********************
1522 * *
1523 * Protected functions *
1524 * *
1525 ***********************/
1526
1527// Return a pointer to a primary or logical partition, or NULL if
1528// the partition is out of range....
srs5694bf8950c2011-03-12 01:23:12 -05001529MBRPart* BasicMBRData::GetPartition(int i) {
1530 MBRPart* thePart = NULL;
srs5694f2efa7d2011-03-01 22:03:26 -05001531
1532 if ((i >= 0) && (i < MAX_MBR_PARTS))
1533 thePart = &partitions[i];
1534 return thePart;
1535} // GetPartition()
srs5694bf8950c2011-03-12 01:23:12 -05001536
1537/*******************************************
1538 * *
1539 * Functions that involve user interaction *
1540 * *
1541 *******************************************/
1542
1543// Present the MBR operations menu. Note that the 'w' option does not
1544// immediately write data; that's handled by the calling function.
1545// Returns the number of partitions defined on exit, or -1 if the
1546// user selected the 'q' option. (Thus, the caller should save data
1547// if the return value is >0, or possibly >=0 depending on intentions.)
1548int BasicMBRData::DoMenu(const string& prompt) {
srs5694bf8950c2011-03-12 01:23:12 -05001549 int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0;
srs56946aae2a92011-06-10 01:16:51 -04001550 unsigned int hexCode;
1551 string tempStr;
srs5694bf8950c2011-03-12 01:23:12 -05001552
1553 do {
1554 cout << prompt;
srs56945a608532011-03-17 13:53:01 -04001555 switch (ReadString()[0]) {
1556 case '\0':
srs5694a6297b82012-03-25 16:13:16 -04001557 goOn = cin.good();
srs5694bf8950c2011-03-12 01:23:12 -05001558 break;
1559 case 'a': case 'A':
1560 num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1;
1561 if (partitions[num].GetInclusion() != NONE)
1562 partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80);
1563 break;
1564 case 'c': case 'C':
1565 for (num = 0; num < MAX_MBR_PARTS; num++)
1566 RecomputeCHS(num);
1567 break;
1568 case 'l': case 'L':
1569 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1;
1570 SetInclusionwChecks(num, LOGICAL);
1571 break;
1572 case 'o': case 'O':
1573 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1;
1574 SetInclusionwChecks(num, NONE);
1575 break;
1576 case 'p': case 'P':
1577 if (!haveShownInfo) {
1578 cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical "
1579 << "status,\n** unlike in most MBR partitioning tools!\n\a";
1580 cout << "\n** Extended partitions are not displayed, but will be generated "
1581 << "as required.\n";
1582 haveShownInfo = 1;
1583 } // if
1584 DisplayMBRData();
1585 break;
1586 case 'q': case 'Q':
1587 cout << "This will abandon your changes. Are you sure? ";
1588 if (GetYN() == 'Y') {
1589 goOn = 0;
1590 quitting = 1;
1591 } // if
1592 break;
1593 case 'r': case 'R':
1594 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1;
1595 SetInclusionwChecks(num, PRIMARY);
1596 break;
1597 case 's': case 'S':
1598 SortMBR();
1599 break;
1600 case 't': case 'T':
1601 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1;
srs56946aae2a92011-06-10 01:16:51 -04001602 hexCode = 0x00;
srs5694bf8950c2011-03-12 01:23:12 -05001603 if (partitions[num].GetLengthLBA() > 0) {
srs5694bf8950c2011-03-12 01:23:12 -05001604 while ((hexCode <= 0) || (hexCode > 255)) {
1605 cout << "Enter an MBR hex code: ";
srs56946aae2a92011-06-10 01:16:51 -04001606 tempStr = ReadString();
1607 if (IsHex(tempStr))
1608 sscanf(tempStr.c_str(), "%x", &hexCode);
srs5694bf8950c2011-03-12 01:23:12 -05001609 } // while
1610 partitions[num].SetType(hexCode);
1611 } // if
1612 break;
1613 case 'w': case 'W':
1614 goOn = 0;
1615 break;
1616 default:
1617 ShowCommands();
1618 break;
1619 } // switch
1620 } while (goOn);
1621 if (quitting)
1622 retval = -1;
1623 else
1624 retval = CountParts();
1625 return (retval);
1626} // BasicMBRData::DoMenu()
1627
1628void BasicMBRData::ShowCommands(void) {
1629 cout << "a\ttoggle the active/boot flag\n";
1630 cout << "c\trecompute all CHS values\n";
1631 cout << "l\tset partition as logical\n";
1632 cout << "o\tomit partition\n";
1633 cout << "p\tprint the MBR partition table\n";
1634 cout << "q\tquit without saving changes\n";
1635 cout << "r\tset partition as primary\n";
1636 cout << "s\tsort MBR partitions\n";
1637 cout << "t\tchange partition type code\n";
1638 cout << "w\twrite the MBR partition table to disk and exit\n";
1639} // BasicMBRData::ShowCommands()