~ubuntu-branches/ubuntu/utopic/gdisk/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/eof_stdin.diff/gpttext.cc

  • Committer: Package Import Robot
  • Author(s): Guillaume Delacour
  • Date: 2012-03-21 23:18:43 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20120321231843-0uep1rjni0ajtsda
Tags: 0.8.2-1
* New upstream release
* debian/patches/manpages.diff:
  + Edit header with DEP3 format
  + Refresh original patch (as version has changed in the manpage)
  + Add an escape on single quote
  + Fix a minor typo (Closes: #651379)
* debian/patches/eof_stdin.diff: avoid infinite loop when ^D in gdisk,
  thanks Gianluigi Tiesi (Closes: #660815)
* debian/control:
  + Remove unnecessary "for" in long description
  (Closes: #644537)
  + Update Depends on debhelper v9
  + Bump to Standards-Version 3.9.3 (no changes needed)
* debian/compat: Update compat to version 9
* debian/copyright: Update url format

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2010-2011  <Roderick W. Smith>
 
3
 
 
4
    This program is free software; you can redistribute it and/or modify
 
5
    it under the terms of the GNU General Public License as published by
 
6
    the Free Software Foundation; either version 2 of the License, or
 
7
    (at your option) any later version.
 
8
 
 
9
    This program is distributed in the hope that it will be useful,
 
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
    GNU General Public License for more details.
 
13
 
 
14
    You should have received a copy of the GNU General Public License along
 
15
    with this program; if not, write to the Free Software Foundation, Inc.,
 
16
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
17
 
 
18
*/
 
19
 
 
20
/* This class implements an interactive text-mode interface atop the
 
21
   GPTData class */
 
22
 
 
23
#include <string.h>
 
24
#include <errno.h>
 
25
#include <stdint.h>
 
26
#include <limits.h>
 
27
#include <iostream>
 
28
#include <sstream>
 
29
#include <cstdio>
 
30
#include "attributes.h"
 
31
#include "gpttext.h"
 
32
#include "support.h"
 
33
 
 
34
using namespace std;
 
35
 
 
36
/********************************************
 
37
 *                                          *
 
38
 * GPTDataText class and related structures *
 
39
 *                                          *
 
40
 ********************************************/
 
41
 
 
42
GPTDataTextUI::GPTDataTextUI(void) : GPTData() {
 
43
} // default constructor
 
44
 
 
45
GPTDataTextUI::GPTDataTextUI(string filename) : GPTData(filename) {
 
46
} // constructor passing filename
 
47
 
 
48
GPTDataTextUI::~GPTDataTextUI(void) {
 
49
} // default destructor
 
50
 
 
51
/*********************************************************************
 
52
 *                                                                   *
 
53
 * The following functions are extended (interactive) versions of    *
 
54
 * simpler functions in the base class....                           *
 
55
 *                                                                   *
 
56
 *********************************************************************/
 
57
 
 
58
// Overridden function; calls base-class function and then makes
 
59
// additional queries of the user, if the base-class function can't
 
60
// decide what to do.
 
61
WhichToUse GPTDataTextUI::UseWhichPartitions(void) {
 
62
   WhichToUse which;
 
63
   MBRValidity mbrState;
 
64
   int answer;
 
65
 
 
66
   which = GPTData::UseWhichPartitions();
 
67
   if ((which != use_abort) || beQuiet)
 
68
      return which;
 
69
 
 
70
   // If we get past here, it means that the non-interactive tests were
 
71
   // inconclusive, so we must ask the user which table to use....
 
72
   mbrState = protectiveMBR.GetValidity();
 
73
 
 
74
   if ((state == gpt_valid) && (mbrState == mbr)) {
 
75
      cout << "Found valid MBR and GPT. Which do you want to use?\n";
 
76
      answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
 
77
      if (answer == 1) {
 
78
         which = use_mbr;
 
79
      } else if (answer == 2) {
 
80
         which = use_gpt;
 
81
         cout << "Using GPT and creating fresh protective MBR.\n";
 
82
      } else which = use_new;
 
83
   } // if
 
84
 
 
85
   // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
 
86
   // problems)
 
87
   if (state == gpt_corrupt) {
 
88
      if ((mbrState == mbr) || (mbrState == hybrid)) {
 
89
         cout << "Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
 
90
              << "GPT MAY permit recovery of GPT data.)\n";
 
91
         answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
 
92
         if (answer == 1) {
 
93
            which = use_mbr;
 
94
         } else if (answer == 2) {
 
95
            which = use_gpt;
 
96
         } else which = use_new;
 
97
      } else if (mbrState == invalid) {
 
98
         cout << "Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
 
99
              << "GPT MAY permit recovery of GPT data.)\n";
 
100
         answer = GetNumber(1, 2, 1, " 1 - Use current GPT\n 2 - Create blank GPT\n\nYour answer: ");
 
101
         if (answer == 1) {
 
102
            which = use_gpt;
 
103
         } else which = use_new;
 
104
      } // if/else/else
 
105
   } // if (corrupt GPT)
 
106
 
 
107
   return which;
 
108
} // UseWhichPartitions()
 
109
 
 
110
// Ask the user for a partition number; and prompt for verification
 
111
// if the requested partition isn't of a known BSD type.
 
112
// Lets the base-class function do the work, and returns its value (the
 
113
// number of converted partitions).
 
114
int GPTDataTextUI::XFormDisklabel(void) {
 
115
   uint32_t partNum;
 
116
   uint16_t hexCode;
 
117
   int goOn = 1, numDone = 0;
 
118
   BSDData disklabel;
 
119
 
 
120
   partNum = GetPartNum();
 
121
 
 
122
   // Now see if the specified partition has a BSD type code....
 
123
   hexCode = partitions[partNum].GetHexType();
 
124
   if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
 
125
      cout << "Specified partition doesn't have a disklabel partition type "
 
126
           << "code.\nContinue anyway? ";
 
127
      goOn = (GetYN() == 'Y');
 
128
   } // if
 
129
 
 
130
   if (goOn)
 
131
      numDone = GPTData::XFormDisklabel(partNum);
 
132
 
 
133
   return numDone;
 
134
} // GPTData::XFormDisklabel(int i)
 
135
 
 
136
 
 
137
/*********************************************************************
 
138
 *                                                                   *
 
139
 * Begin functions that obtain information from the users, and often *
 
140
 * do something with that information (call other functions)         *
 
141
 *                                                                   *
 
142
 *********************************************************************/
 
143
 
 
144
// Prompts user for partition number and returns the result. Returns "0"
 
145
// (the first partition) if none are currently defined.
 
146
uint32_t GPTDataTextUI::GetPartNum(void) {
 
147
   uint32_t partNum;
 
148
   uint32_t low, high;
 
149
   ostringstream prompt;
 
150
 
 
151
   if (GetPartRange(&low, &high) > 0) {
 
152
      prompt << "Partition number (" << low + 1 << "-" << high + 1 << "): ";
 
153
      partNum = GetNumber(low + 1, high + 1, low, prompt.str());
 
154
   } else partNum = 1;
 
155
   return (partNum - 1);
 
156
} // GPTDataTextUI::GetPartNum()
 
157
 
 
158
// What it says: Resize the partition table. (Default is 128 entries.)
 
159
void GPTDataTextUI::ResizePartitionTable(void) {
 
160
   int newSize;
 
161
   ostringstream prompt;
 
162
   uint32_t curLow, curHigh;
 
163
 
 
164
   cout << "Current partition table size is " << numParts << ".\n";
 
165
   GetPartRange(&curLow, &curHigh);
 
166
   curHigh++; // since GetPartRange() returns numbers starting from 0...
 
167
   // There's no point in having fewer than four partitions....
 
168
   if (curHigh < (blockSize / GPT_SIZE))
 
169
      curHigh = blockSize / GPT_SIZE;
 
170
   prompt << "Enter new size (" << curHigh << " up, default " << NUM_GPT_ENTRIES << "): ";
 
171
   newSize = GetNumber(4, 65535, 128, prompt.str());
 
172
   if (newSize < 128) {
 
173
      cout << "Caution: The partition table size should officially be 16KB or larger,\n"
 
174
           << "which works out to 128 entries. In practice, smaller tables seem to\n"
 
175
           << "work with most OSes, but this practice is risky. I'm proceeding with\n"
 
176
           << "the resize, but you may want to reconsider this action and undo it.\n\n";
 
177
   } // if
 
178
   SetGPTSize(newSize);
 
179
} // GPTDataTextUI::ResizePartitionTable()
 
180
 
 
181
// Interactively create a partition
 
182
void GPTDataTextUI::CreatePartition(void) {
 
183
   uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector;
 
184
   uint32_t firstFreePart = 0;
 
185
   ostringstream prompt1, prompt2, prompt3;
 
186
   int partNum;
 
187
 
 
188
   // Find first free partition...
 
189
   while (partitions[firstFreePart].GetFirstLBA() != 0) {
 
190
      firstFreePart++;
 
191
   } // while
 
192
 
 
193
   if (((firstBlock = FindFirstAvailable()) != 0) &&
 
194
       (firstFreePart < numParts)) {
 
195
      lastBlock = FindLastAvailable();
 
196
      firstInLargest = FindFirstInLargest();
 
197
      Align(&firstInLargest);
 
198
 
 
199
      // Get partition number....
 
200
      do {
 
201
         prompt1 << "Partition number (" << firstFreePart + 1 << "-" << numParts
 
202
                 << ", default " << firstFreePart + 1 << "): ";
 
203
         partNum = GetNumber(firstFreePart + 1, numParts,
 
204
                             firstFreePart + 1, prompt1.str()) - 1;
 
205
         if (partitions[partNum].GetFirstLBA() != 0)
 
206
            cout << "partition " << partNum + 1 << " is in use.\n";
 
207
      } while (partitions[partNum].GetFirstLBA() != 0);
 
208
 
 
209
      // Get first block for new partition...
 
210
      prompt2 << "First sector (" << firstBlock << "-" << lastBlock << ", default = "
 
211
              << firstInLargest << ") or {+-}size{KMGTP}: ";
 
212
      do {
 
213
         sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, blockSize, prompt2.str());
 
214
      } while (IsFree(sector) == 0);
 
215
      origSector = sector;
 
216
      if (Align(&sector)) {
 
217
         cout << "Information: Moved requested sector from " << origSector << " to "
 
218
              << sector << " in\norder to align on " << sectorAlignment
 
219
              << "-sector boundaries.\n";
 
220
         if (!beQuiet)
 
221
            cout << "Use 'l' on the experts' menu to adjust alignment\n";
 
222
      } // if
 
223
      //      Align(&sector); // Align sector to correct multiple
 
224
      firstBlock = sector;
 
225
 
 
226
      // Get last block for new partitions...
 
227
      lastBlock = FindLastInFree(firstBlock);
 
228
      prompt3 << "Last sector (" << firstBlock << "-" << lastBlock << ", default = "
 
229
            << lastBlock << ") or {+-}size{KMGTP}: ";
 
230
      do {
 
231
         sector = GetSectorNum(firstBlock, lastBlock, lastBlock, blockSize, prompt3.str());
 
232
      } while (IsFree(sector) == 0);
 
233
      lastBlock = sector;
 
234
 
 
235
      firstFreePart = GPTData::CreatePartition(partNum, firstBlock, lastBlock);
 
236
      partitions[partNum].ChangeType();
 
237
      partitions[partNum].SetDefaultDescription();
 
238
   } else {
 
239
      if (firstFreePart >= numParts)
 
240
         cout << "No table partition entries left\n";
 
241
      else
 
242
         cout << "No free sectors available\n";
 
243
   } // if/else
 
244
} // GPTDataTextUI::CreatePartition()
 
245
 
 
246
// Interactively delete a partition (duh!)
 
247
void GPTDataTextUI::DeletePartition(void) {
 
248
   int partNum;
 
249
   uint32_t low, high;
 
250
   ostringstream prompt;
 
251
 
 
252
   if (GetPartRange(&low, &high) > 0) {
 
253
      prompt << "Partition number (" << low + 1 << "-" << high + 1 << "): ";
 
254
      partNum = GetNumber(low + 1, high + 1, low, prompt.str());
 
255
      GPTData::DeletePartition(partNum - 1);
 
256
   } else {
 
257
      cout << "No partitions\n";
 
258
   } // if/else
 
259
} // GPTDataTextUI::DeletePartition()
 
260
 
 
261
// Prompt user for a partition number, then change its type code
 
262
void GPTDataTextUI::ChangePartType(void) {
 
263
   int partNum;
 
264
   uint32_t low, high;
 
265
 
 
266
   if (GetPartRange(&low, &high) > 0) {
 
267
      partNum = GetPartNum();
 
268
      partitions[partNum].ChangeType();
 
269
   } else {
 
270
      cout << "No partitions\n";
 
271
   } // if/else
 
272
} // GPTDataTextUI::ChangePartType()
 
273
 
 
274
// Prompt user for a partition number, then change its unique
 
275
// GUID.
 
276
void GPTDataTextUI::ChangeUniqueGuid(void) {
 
277
   int partNum;
 
278
   uint32_t low, high;
 
279
   string guidStr;
 
280
 
 
281
   if (GetPartRange(&low, &high) > 0) {
 
282
      partNum = GetPartNum();
 
283
      cout << "Enter the partition's new unique GUID ('R' to randomize): ";
 
284
      guidStr = ReadString();
 
285
      if ((guidStr.length() >= 32) || (guidStr[0] == 'R') || (guidStr[0] == 'r')) {
 
286
         SetPartitionGUID(partNum, (GUIDData) guidStr);
 
287
         cout << "New GUID is " << partitions[partNum].GetUniqueGUID() << "\n";
 
288
      } else {
 
289
         cout << "GUID is too short!\n";
 
290
      } // if/else
 
291
   } else
 
292
      cout << "No partitions\n";
 
293
} // GPTDataTextUI::ChangeUniqueGuid()
 
294
 
 
295
// Partition attributes seem to be rarely used, but I want a way to
 
296
// adjust them for completeness....
 
297
void GPTDataTextUI::SetAttributes(uint32_t partNum) {
 
298
   partitions[partNum].SetAttributes();
 
299
} // GPTDataTextUI::SetAttributes()
 
300
 
 
301
// Prompts the user for a partition name and sets the partition's
 
302
// name. Returns 1 on success, 0 on failure (invalid partition
 
303
// number). (Note that the function skips prompting when an
 
304
// invalid partition number is detected.)
 
305
int GPTDataTextUI::SetName(uint32_t partNum) {
 
306
   UnicodeString theName = "";
 
307
   int retval = 1;
 
308
 
 
309
   if (IsUsedPartNum(partNum)) {
 
310
      cout << "Enter name: ";
 
311
      theName = ReadUString();
 
312
      partitions[partNum].SetName(theName);
 
313
   } else {
 
314
      cerr << "Invalid partition number (" << partNum << ")\n";
 
315
      retval = 0;
 
316
   } // if/else
 
317
 
 
318
   return retval;
 
319
} // GPTDataTextUI::SetName()
 
320
 
 
321
// Ask user for two partition numbers and swap them in the table. Note that
 
322
// this just reorders table entries; it doesn't adjust partition layout on
 
323
// the disk.
 
324
// Returns 1 if successful, 0 if not. (If user enters identical numbers, it
 
325
// counts as successful.)
 
326
int GPTDataTextUI::SwapPartitions(void) {
 
327
   int partNum1, partNum2, didIt = 0;
 
328
   uint32_t low, high;
 
329
   ostringstream prompt;
 
330
   GPTPart temp;
 
331
 
 
332
   if (GetPartRange(&low, &high) > 0) {
 
333
      partNum1 = GetPartNum();
 
334
      if (high >= numParts - 1)
 
335
         high = 0;
 
336
      prompt << "New partition number (1-" << numParts
 
337
             << ", default " << high + 2 << "): ";
 
338
      partNum2 = GetNumber(1, numParts, high + 2, prompt.str()) - 1;
 
339
      didIt = GPTData::SwapPartitions(partNum1, partNum2);
 
340
   } else {
 
341
      cout << "No partitions\n";
 
342
   } // if/else
 
343
   return didIt;
 
344
} // GPTDataTextUI::SwapPartitionNumbers()
 
345
 
 
346
// This function destroys the on-disk GPT structures. Returns 1 if the user
 
347
// confirms destruction, 0 if the user aborts or if there's a disk error.
 
348
int GPTDataTextUI::DestroyGPTwPrompt(void) {
 
349
   int allOK = 1;
 
350
 
 
351
   if ((apmFound) || (bsdFound)) {
 
352
      cout << "WARNING: APM or BSD disklabel structures detected! This operation could\n"
 
353
           << "damage any APM or BSD partitions on this disk!\n";
 
354
   } // if APM or BSD
 
355
   cout << "\a\aAbout to wipe out GPT on " << device << ". Proceed? ";
 
356
   if (GetYN() == 'Y') {
 
357
      if (DestroyGPT()) {
 
358
         // Note on below: Touch the MBR only if the user wants it completely
 
359
         // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
 
360
         // the MBR, but this could wipe out a valid MBR that the program
 
361
         // had subsequently discarded (say, if it conflicted with older GPT
 
362
         // structures).
 
363
         cout << "Blank out MBR? ";
 
364
         if (GetYN() == 'Y') {
 
365
            DestroyMBR();
 
366
         } else {
 
367
            cout << "MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
 
368
                 << "with fdisk or another tool.\n";
 
369
         } // if/else
 
370
      } else allOK = 0; // if GPT structures destroyed
 
371
   } else allOK = 0; // if user confirms destruction
 
372
   return (allOK);
 
373
} // GPTDataTextUI::DestroyGPTwPrompt()
 
374
 
 
375
// Get partition number from user and then call ShowPartDetails(partNum)
 
376
// to show its detailed information
 
377
void GPTDataTextUI::ShowDetails(void) {
 
378
   int partNum;
 
379
   uint32_t low, high;
 
380
 
 
381
   if (GetPartRange(&low, &high) > 0) {
 
382
      partNum = GetPartNum();
 
383
      ShowPartDetails(partNum);
 
384
   } else {
 
385
      cout << "No partitions\n";
 
386
   } // if/else
 
387
} // GPTDataTextUI::ShowDetails()
 
388
 
 
389
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
 
390
// OSes that don't understand GPT.
 
391
void GPTDataTextUI::MakeHybrid(void) {
 
392
   uint32_t partNums[3];
 
393
   string line;
 
394
   int numPartsToCvt, i, j, mbrNum = 0;
 
395
   unsigned int hexCode = 0;
 
396
   MBRPart hybridPart;
 
397
   MBRData hybridMBR;
 
398
   char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
 
399
 
 
400
   cout << "\nWARNING! Hybrid MBRs are flaky and dangerous! If you decide not to use one,\n"
 
401
        << "just hit the Enter key at the below prompt and your MBR partition table will\n"
 
402
        << "be untouched.\n\n\a";
 
403
 
 
404
   // Use a local MBR structure, copying from protectiveMBR to keep its
 
405
   // boot loader code intact....
 
406
   hybridMBR = protectiveMBR;
 
407
   hybridMBR.EmptyMBR(0);
 
408
 
 
409
   // Now get the numbers of up to three partitions to add to the
 
410
   // hybrid MBR....
 
411
   cout << "Type from one to three GPT partition numbers, separated by spaces, to be\n"
 
412
        << "added to the hybrid MBR, in sequence: ";
 
413
   line = ReadString();
 
414
   numPartsToCvt = sscanf(line.c_str(), "%d %d %d", &partNums[0], &partNums[1], &partNums[2]);
 
415
 
 
416
   if (numPartsToCvt > 0) {
 
417
      cout << "Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ";
 
418
      eeFirst = GetYN();
 
419
   } // if
 
420
 
 
421
   for (i = 0; i < numPartsToCvt; i++) {
 
422
      j = partNums[i] - 1;
 
423
      if (partitions[j].IsUsed()) {
 
424
         mbrNum = i + (eeFirst == 'Y');
 
425
         cout << "\nCreating entry for GPT partition #" << j + 1
 
426
              << " (MBR partition #" << mbrNum + 1 << ")\n";
 
427
         hybridPart.SetType(GetMBRTypeCode(partitions[j].GetHexType() / 256));
 
428
         hybridPart.SetLocation(partitions[j].GetFirstLBA(), partitions[j].GetLengthLBA());
 
429
         hybridPart.SetInclusion(PRIMARY);
 
430
         cout << "Set the bootable flag? ";
 
431
         if (GetYN() == 'Y')
 
432
            hybridPart.SetStatus(0x80);
 
433
         else
 
434
            hybridPart.SetStatus(0x00);
 
435
         hybridPart.SetInclusion(PRIMARY);
 
436
      } else {
 
437
         cerr << "\nGPT partition #" << j + 1 << " does not exist; skipping.\n";
 
438
      } // if/else
 
439
      hybridMBR.AddPart(mbrNum, hybridPart);
 
440
   } // for
 
441
 
 
442
   if (numPartsToCvt > 0) { // User opted to create a hybrid MBR....
 
443
      // Create EFI protective partition that covers the start of the disk.
 
444
      // If this location (covering the main GPT data structures) is omitted,
 
445
      // Linux won't find any partitions on the disk.
 
446
      hybridPart.SetLocation(1, hybridMBR.FindLastInFree(1));
 
447
      hybridPart.SetStatus(0);
 
448
      hybridPart.SetType(0xEE);
 
449
      hybridPart.SetInclusion(PRIMARY);
 
450
      // newNote firstLBA and lastLBA are computed later...
 
451
      if (eeFirst == 'Y') {
 
452
         hybridMBR.AddPart(0, hybridPart);
 
453
      } else {
 
454
         hybridMBR.AddPart(3, hybridPart);
 
455
      } // else
 
456
      hybridMBR.SetHybrid();
 
457
 
 
458
      // ... and for good measure, if there are any partition spaces left,
 
459
      // optionally create another protective EFI partition to cover as much
 
460
      // space as possible....
 
461
      if (hybridMBR.CountParts() < 4) { // unused entry....
 
462
         cout << "\nUnused partition space(s) found. Use one to protect more partitions? ";
 
463
         if (GetYN() == 'Y') {
 
464
            cout << "Note: Default is 0xEE, but this may confuse Mac OS X.\n";
 
465
            // Comment on above: Mac OS treats disks with more than one
 
466
            // 0xEE MBR partition as MBR disks, not as GPT disks.
 
467
            hexCode = GetMBRTypeCode(0xEE);
 
468
            hybridMBR.MakeBiggestPart(3, hexCode);
 
469
         } // if (GetYN() == 'Y')
 
470
      } // if unused entry
 
471
      protectiveMBR = hybridMBR;
 
472
   } // if (numPartsToCvt > 0)
 
473
} // GPTDataTextUI::MakeHybrid()
 
474
 
 
475
// Convert the GPT to MBR form, storing partitions in the protectiveMBR
 
476
// variable. This function is necessarily limited; it may not be able to
 
477
// convert all partitions, depending on the disk size and available space
 
478
// before each partition (one free sector is required to create a logical
 
479
// partition, which are necessary to convert more than four partitions).
 
480
// Returns the number of converted partitions; if this value
 
481
// is over 0, the calling function should call DestroyGPT() to destroy
 
482
// the GPT data, call SaveMBR() to save the MBR, and then exit.
 
483
int GPTDataTextUI::XFormToMBR(void) {
 
484
   uint32_t i;
 
485
 
 
486
   protectiveMBR.EmptyMBR(0);
 
487
   for (i = 0; i < numParts; i++) {
 
488
      if (partitions[i].IsUsed()) {
 
489
         protectiveMBR.MakePart(i, partitions[i].GetFirstLBA(),
 
490
                                partitions[i].GetLengthLBA(),
 
491
                                partitions[i].GetHexType() / 0x0100, 0);
 
492
      } // if
 
493
   } // for
 
494
   protectiveMBR.MakeItLegal();
 
495
   return protectiveMBR.DoMenu();
 
496
} // GPTDataTextUI::XFormToMBR()
 
497
 
 
498
 
 
499
/*********************************************************************
 
500
 *                                                                   *
 
501
 * The following functions provide the main menus for the gdisk      *
 
502
 * program....                                                       *
 
503
 *                                                                   *
 
504
 *********************************************************************/
 
505
 
 
506
// Accept a command and execute it. Returns only when the user
 
507
// wants to exit (such as after a 'w' or 'q' command).
 
508
void GPTDataTextUI::MainMenu(string filename) {
 
509
   int goOn = 1;
 
510
   PartType typeHelper;
 
511
   uint32_t temp1, temp2;
 
512
   
 
513
   do {
 
514
      cout << "\nCommand (? for help): ";
 
515
      switch (ReadString()[0]) {
 
516
         case '\0':
 
517
            break;
 
518
         case 'b': case 'B':
 
519
            cout << "Enter backup filename to save: ";
 
520
            SaveGPTBackup(ReadString());
 
521
            break;
 
522
         case 'c': case 'C':
 
523
            if (GetPartRange(&temp1, &temp2) > 0)
 
524
               SetName(GetPartNum());
 
525
            else
 
526
               cout << "No partitions\n";
 
527
            break;
 
528
         case 'd': case 'D':
 
529
            DeletePartition();
 
530
            break;
 
531
         case 'i': case 'I':
 
532
            ShowDetails();
 
533
            break;
 
534
         case 'l': case 'L':
 
535
            typeHelper.ShowAllTypes();
 
536
            break;
 
537
         case 'n': case 'N':
 
538
            CreatePartition();
 
539
            break;
 
540
         case 'o': case 'O':
 
541
            cout << "This option deletes all partitions and creates a new protective MBR.\n"
 
542
                 << "Proceed? ";
 
543
            if (GetYN() == 'Y') {
 
544
               ClearGPTData();
 
545
               MakeProtectiveMBR();
 
546
            } // if
 
547
            break;
 
548
         case 'p': case 'P':
 
549
            DisplayGPTData();
 
550
            break;
 
551
         case 'q': case 'Q':
 
552
            goOn = 0;
 
553
            break;
 
554
         case 'r': case 'R':
 
555
            RecoveryMenu(filename);
 
556
            goOn = 0;
 
557
            break;
 
558
         case 's': case 'S':
 
559
            SortGPT();
 
560
            cout << "You may need to edit /etc/fstab and/or your boot loader configuration!\n";
 
561
            break;
 
562
         case 't': case 'T':
 
563
            ChangePartType();
 
564
            break;
 
565
         case 'v': case 'V':
 
566
            Verify();
 
567
            break;
 
568
         case 'w': case 'W':
 
569
            if (SaveGPTData() == 1)
 
570
               goOn = 0;
 
571
            break;
 
572
         case 'x': case 'X':
 
573
            ExpertsMenu(filename);
 
574
            goOn = 0;
 
575
            break;
 
576
         default:
 
577
            ShowCommands();
 
578
            break;
 
579
      } // switch
 
580
   } while (goOn);
 
581
} // GPTDataTextUI::MainMenu()
 
582
 
 
583
void GPTDataTextUI::ShowCommands(void) {
 
584
   cout << "b\tback up GPT data to a file\n";
 
585
   cout << "c\tchange a partition's name\n";
 
586
   cout << "d\tdelete a partition\n";
 
587
   cout << "i\tshow detailed information on a partition\n";
 
588
   cout << "l\tlist known partition types\n";
 
589
   cout << "n\tadd a new partition\n";
 
590
   cout << "o\tcreate a new empty GUID partition table (GPT)\n";
 
591
   cout << "p\tprint the partition table\n";
 
592
   cout << "q\tquit without saving changes\n";
 
593
   cout << "r\trecovery and transformation options (experts only)\n";
 
594
   cout << "s\tsort partitions\n";
 
595
   cout << "t\tchange a partition's type code\n";
 
596
   cout << "v\tverify disk\n";
 
597
   cout << "w\twrite table to disk and exit\n";
 
598
   cout << "x\textra functionality (experts only)\n";
 
599
   cout << "?\tprint this menu\n";
 
600
} // GPTDataTextUI::ShowCommands()
 
601
 
 
602
// Accept a recovery & transformation menu command. Returns only when the user
 
603
// issues an exit command, such as 'w' or 'q'.
 
604
void GPTDataTextUI::RecoveryMenu(string filename) {
 
605
   uint32_t numParts;
 
606
   int goOn = 1, temp1;
 
607
   
 
608
   do {
 
609
      cout << "\nRecovery/transformation command (? for help): ";
 
610
      switch (ReadString()[0]) {
 
611
         case '\0':
 
612
            break;
 
613
         case 'b': case 'B':
 
614
            RebuildMainHeader();
 
615
            break;
 
616
         case 'c': case 'C':
 
617
            cout << "Warning! This will probably do weird things if you've converted an MBR to\n"
 
618
            << "GPT form and haven't yet saved the GPT! Proceed? ";
 
619
            if (GetYN() == 'Y')
 
620
               LoadSecondTableAsMain();
 
621
            break;
 
622
         case 'd': case 'D':
 
623
            RebuildSecondHeader();
 
624
            break;
 
625
         case 'e': case 'E':
 
626
            cout << "Warning! This will probably do weird things if you've converted an MBR to\n"
 
627
            << "GPT form and haven't yet saved the GPT! Proceed? ";
 
628
            if (GetYN() == 'Y')
 
629
               LoadMainTable();
 
630
            break;
 
631
         case 'f': case 'F':
 
632
            cout << "Warning! This will destroy the currently defined partitions! Proceed? ";
 
633
            if (GetYN() == 'Y') {
 
634
               if (LoadMBR(filename) == 1) { // successful load
 
635
                  XFormPartitions();
 
636
               } else {
 
637
                  cout << "Problem loading MBR! GPT is untouched; regenerating protective MBR!\n";
 
638
                  MakeProtectiveMBR();
 
639
               } // if/else
 
640
            } // if
 
641
            break;
 
642
         case 'g': case 'G':
 
643
            numParts = GetNumParts();
 
644
            temp1 = XFormToMBR();
 
645
            if (temp1 > 0)
 
646
               cout << "\nConverted " << temp1 << " partitions. Finalize and exit? ";
 
647
            if ((temp1 > 0) && (GetYN() == 'Y')) {
 
648
               if ((DestroyGPT() > 0) && (SaveMBR())) {
 
649
                  goOn = 0;
 
650
               } // if
 
651
            } else {
 
652
               MakeProtectiveMBR();
 
653
               SetGPTSize(numParts, 0);
 
654
               cout << "Note: New protective MBR created\n\n";
 
655
            } // if/else
 
656
            break;
 
657
         case 'h': case 'H':
 
658
            MakeHybrid();
 
659
            break;
 
660
         case 'i': case 'I':
 
661
            ShowDetails();
 
662
            break;
 
663
         case 'l': case 'L':
 
664
            cout << "Enter backup filename to load: ";
 
665
            LoadGPTBackup(ReadString());
 
666
            break;
 
667
         case 'm': case 'M':
 
668
            MainMenu(filename);
 
669
            goOn = 0;
 
670
            break;
 
671
         case 'o': case 'O':
 
672
            DisplayMBRData();
 
673
            break;
 
674
         case 'p': case 'P':
 
675
            DisplayGPTData();
 
676
            break;
 
677
         case 'q': case 'Q':
 
678
            goOn = 0;
 
679
            break;
 
680
         case 't': case 'T':
 
681
            XFormDisklabel();
 
682
            break;
 
683
         case 'v': case 'V':
 
684
            Verify();
 
685
            break;
 
686
         case 'w': case 'W':
 
687
            if (SaveGPTData() == 1) {
 
688
               goOn = 0;
 
689
            } // if
 
690
            break;
 
691
         case 'x': case 'X':
 
692
            ExpertsMenu(filename);
 
693
            goOn = 0;
 
694
            break;
 
695
         default:
 
696
            ShowRecoveryCommands();
 
697
            break;
 
698
      } // switch
 
699
   } while (goOn);
 
700
} // GPTDataTextUI::RecoveryMenu()
 
701
 
 
702
void GPTDataTextUI::ShowRecoveryCommands(void) {
 
703
   cout << "b\tuse backup GPT header (rebuilding main)\n";
 
704
   cout << "c\tload backup partition table from disk (rebuilding main)\n";
 
705
   cout << "d\tuse main GPT header (rebuilding backup)\n";
 
706
   cout << "e\tload main partition table from disk (rebuilding backup)\n";
 
707
   cout << "f\tload MBR and build fresh GPT from it\n";
 
708
   cout << "g\tconvert GPT into MBR and exit\n";
 
709
   cout << "h\tmake hybrid MBR\n";
 
710
   cout << "i\tshow detailed information on a partition\n";
 
711
   cout << "l\tload partition data from a backup file\n";
 
712
   cout << "m\treturn to main menu\n";
 
713
   cout << "o\tprint protective MBR data\n";
 
714
   cout << "p\tprint the partition table\n";
 
715
   cout << "q\tquit without saving changes\n";
 
716
   cout << "t\ttransform BSD disklabel partition\n";
 
717
   cout << "v\tverify disk\n";
 
718
   cout << "w\twrite table to disk and exit\n";
 
719
   cout << "x\textra functionality (experts only)\n";
 
720
   cout << "?\tprint this menu\n";
 
721
} // GPTDataTextUI::ShowRecoveryCommands()
 
722
 
 
723
// Accept an experts' menu command. Returns only after the user
 
724
// selects an exit command, such as 'w' or 'q'.
 
725
void GPTDataTextUI::ExpertsMenu(string filename) {
 
726
   GPTData secondDevice;
 
727
   uint32_t temp1, temp2;
 
728
   int goOn = 1;
 
729
   string guidStr, device;
 
730
   GUIDData aGUID;
 
731
   ostringstream prompt;
 
732
   
 
733
   do {
 
734
      cout << "\nExpert command (? for help): ";
 
735
      switch (ReadString()[0]) {
 
736
         case '\0':
 
737
            break;
 
738
         case 'a': case 'A':
 
739
            if (GetPartRange(&temp1, &temp2) > 0)
 
740
               SetAttributes(GetPartNum());
 
741
            else
 
742
               cout << "No partitions\n";
 
743
            break;
 
744
         case 'c': case 'C':
 
745
            ChangeUniqueGuid();
 
746
            break;
 
747
         case 'd': case 'D':
 
748
            cout << "Partitions will begin on " << GetAlignment()
 
749
            << "-sector boundaries.\n";
 
750
            break;
 
751
         case 'e': case 'E':
 
752
            cout << "Relocating backup data structures to the end of the disk\n";
 
753
            MoveSecondHeaderToEnd();
 
754
            break;
 
755
         case 'f': case 'F':
 
756
            RandomizeGUIDs();
 
757
            break;
 
758
         case 'g': case 'G':
 
759
            cout << "Enter the disk's unique GUID ('R' to randomize): ";
 
760
            guidStr = ReadString();
 
761
            if ((guidStr.length() >= 32) || (guidStr[0] == 'R') || (guidStr[0] == 'r')) {
 
762
               SetDiskGUID((GUIDData) guidStr);
 
763
               cout << "The new disk GUID is " << GetDiskGUID() << "\n";
 
764
            } else {
 
765
               cout << "GUID is too short!\n";
 
766
            } // if/else
 
767
            break;
 
768
         case 'h': case 'H':
 
769
            RecomputeCHS();
 
770
            break;
 
771
         case 'i': case 'I':
 
772
            ShowDetails();
 
773
            break;
 
774
         case 'l': case 'L':
 
775
            prompt.seekp(0);
 
776
            prompt << "Enter the sector alignment value (1-" << MAX_ALIGNMENT << ", default = "
 
777
                   << DEFAULT_ALIGNMENT << "): ";
 
778
            temp1 = GetNumber(1, MAX_ALIGNMENT, DEFAULT_ALIGNMENT, prompt.str());
 
779
            SetAlignment(temp1);
 
780
            break;
 
781
         case 'm': case 'M':
 
782
            MainMenu(filename);
 
783
            goOn = 0;
 
784
            break;
 
785
         case 'n': case 'N':
 
786
            MakeProtectiveMBR();
 
787
            break;
 
788
         case 'o': case 'O':
 
789
            DisplayMBRData();
 
790
            break;
 
791
         case 'p': case 'P':
 
792
            DisplayGPTData();
 
793
            break;
 
794
         case 'q': case 'Q':
 
795
            goOn = 0;
 
796
            break;
 
797
         case 'r': case 'R':
 
798
            RecoveryMenu(filename);
 
799
            goOn = 0;
 
800
            break;
 
801
         case 's': case 'S':
 
802
            ResizePartitionTable();
 
803
            break;
 
804
         case 't': case 'T':
 
805
            SwapPartitions();
 
806
            break;
 
807
         case 'u': case 'U':
 
808
            cout << "Type device filename, or press <Enter> to exit: ";
 
809
            device = ReadString();
 
810
            if (device.length() > 0) {
 
811
               secondDevice = *this;
 
812
               secondDevice.SetDisk(device);
 
813
               secondDevice.SaveGPTData(0);
 
814
            } // if
 
815
            break;
 
816
         case 'v': case 'V':
 
817
            Verify();
 
818
            break;
 
819
         case 'w': case 'W':
 
820
            if (SaveGPTData() == 1) {
 
821
               goOn = 0;
 
822
            } // if
 
823
            break;
 
824
         case 'z': case 'Z':
 
825
            if (DestroyGPTwPrompt() == 1) {
 
826
               goOn = 0;
 
827
            }
 
828
            break;
 
829
         default:
 
830
            ShowExpertCommands();
 
831
            break;
 
832
      } // switch
 
833
   } while (goOn);
 
834
} // GPTDataTextUI::ExpertsMenu()
 
835
 
 
836
void GPTDataTextUI::ShowExpertCommands(void) {
 
837
   cout << "a\tset attributes\n";
 
838
   cout << "c\tchange partition GUID\n";
 
839
   cout << "d\tdisplay the sector alignment value\n";
 
840
   cout << "e\trelocate backup data structures to the end of the disk\n";
 
841
   cout << "g\tchange disk GUID\n";
 
842
   cout << "h\trecompute CHS values in protective/hybrid MBR\n";
 
843
   cout << "i\tshow detailed information on a partition\n";
 
844
   cout << "l\tset the sector alignment value\n";
 
845
   cout << "m\treturn to main menu\n";
 
846
   cout << "n\tcreate a new protective MBR\n";
 
847
   cout << "o\tprint protective MBR data\n";
 
848
   cout << "p\tprint the partition table\n";
 
849
   cout << "q\tquit without saving changes\n";
 
850
   cout << "r\trecovery and transformation options (experts only)\n";
 
851
   cout << "s\tresize partition table\n";
 
852
   cout << "t\ttranspose two partition table entries\n";
 
853
   cout << "u\tReplicate partition table on new device\n";
 
854
   cout << "v\tverify disk\n";
 
855
   cout << "w\twrite table to disk and exit\n";
 
856
   cout << "z\tzap (destroy) GPT data structures and exit\n";
 
857
   cout << "?\tprint this menu\n";
 
858
} // GPTDataTextUI::ShowExpertCommands()
 
859
 
 
860
 
 
861
 
 
862
/********************************
 
863
 *                              *
 
864
 * Non-class support functions. *
 
865
 *                              *
 
866
 ********************************/
 
867
 
 
868
// GetMBRTypeCode() doesn't really belong in the class, since it's MBR-
 
869
// specific, but it's also user I/O-related, so I want to keep it in
 
870
// this file....
 
871
 
 
872
// Get an MBR type code from the user and return it
 
873
int GetMBRTypeCode(int defType) {
 
874
   string line;
 
875
   int typeCode;
 
876
 
 
877
   cout.setf(ios::uppercase);
 
878
   cout.fill('0');
 
879
   do {
 
880
      cout << "Enter an MBR hex code (default " << hex;
 
881
      cout.width(2);
 
882
      cout << defType << "): " << dec;
 
883
      line = ReadString();
 
884
      if (line[0] == '\0')
 
885
         typeCode = defType;
 
886
      else
 
887
         typeCode = StrToHex(line, 0);
 
888
   } while ((typeCode <= 0) || (typeCode > 255));
 
889
   cout.fill(' ');
 
890
   return typeCode;
 
891
} // GetMBRTypeCode
 
892
 
 
893
// Note: ReadUString() is here rather than in support.cc so that the ICU
 
894
// libraries need not be linked to fixparts.
 
895
 
 
896
// Reads a Unicode string from stdin, returning it as an ICU-style string.
 
897
// Note that the returned string will NOT include the carriage return
 
898
// entered by the user. Relies on the ICU constructor from a string
 
899
// encoded in the current codepage to work.
 
900
UnicodeString ReadUString(void) {
 
901
   return ReadString().c_str();
 
902
} // ReadUString()
 
903