/*-
* Copyright (C) 2008 Erik Larsson
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catacombae.hfsexplorer.testcode;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import org.catacombae.storage.ps.gpt.types.GPTHeader;
import org.catacombae.storage.ps.gpt.types.GUIDPartitionTable;
import org.catacombae.storage.ps.gpt.types.MutableGPTEntry;
import org.catacombae.storage.ps.gpt.types.MutableGPTHeader;
import org.catacombae.storage.ps.gpt.types.MutableGUIDPartitionTable;
import org.catacombae.storage.io.win32.Win32FileStream;
import org.catacombae.io.FileStream;
import org.catacombae.io.RandomAccessStream;
import org.catacombae.io.TruncatableRandomAccess;
import org.catacombae.util.Util;
/**
* This application reads the primary GPT header from the specified disk and writes a
* corresponding backup header at the end of the disk. Useful for when a disk image
* does not contain the entire length of the drive, due to read errors and such.
*/
public class RepairMyBackupGPT {
private static BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws Exception {
long runTimeStamp = System.currentTimeMillis();
RandomAccessStream llf;
if(System.getProperty("os.name").toLowerCase().startsWith("windows") &&
!new File(args[0]).exists()) {
System.out.println("Win32 extended stream loading...");
llf = new Win32FileStream(args[0]); // In case we have specified a windows device path
}
else
llf = new FileStream(args[0]);
final GUIDPartitionTable originalGpt = new GUIDPartitionTable(llf, 0);
MutableGUIDPartitionTable gpt = new MutableGUIDPartitionTable(originalGpt);
if(!originalGpt.isValid() && !gpt.isValid()) {
final int blockSize = 512;
GPTHeader hdr = gpt.getHeader();
GPTHeader backupHdr = gpt.getBackupHeader();
if(!hdr.isValid()) {
System.out.println("The GPT primary header is not valid! Exiting...");
System.exit(-1);
return;
}
else if(backupHdr.isValid()) {
System.out.println("The backup header is already valid. Exiting...");
System.exit(-1);
return;
}
// Backup the GPT backup table at the end of the disk.
String backupFilename2 = null;
{
long backupGPTPos = hdr.getBackupLBA()*blockSize - hdr.getNumberOfPartitionEntries()*hdr.getSizeOfPartitionEntry();
int backupGPTLen = hdr.getNumberOfPartitionEntries()*hdr.getSizeOfPartitionEntry() + blockSize;
byte[] backup2 = new byte[backupGPTLen];
llf.seek(backupGPTPos);
int bytesRead = llf.read(backup2);
if(bytesRead > 0) {
backupFilename2 = "gpt_backup_table-" + runTimeStamp + ".backup";
System.out.print("Backing up GPT backup header and table to \"" + backupFilename2 + "\"...");
FileOutputStream backupFile2 = new FileOutputStream(backupFilename2);
backupFile2.write(backup2, 0, bytesRead);
backupFile2.close();
System.out.println("done!");
}
else {
System.out.println("Could not read the backup GPT table data area!");
String s = "";
while(!(s.equals("y") || s.equals("n"))) {
System.out.print("Continue without backup (y/n)? ");
s = stdin.readLine();
}
if(s.equals("n")) {
System.exit(-1);
return;
}
}
}
// Now let's modify the table in memory
System.out.println("Modifying GPT data in memory:");
System.out.print(" - Creating a GPT backup header from the primary header...");
GPTHeader newBackupHeader = hdr.createValidBackupHeader();
System.out.println("done.");
System.out.print(" - Setting contents of mutable backup header to newly created backup header...");
MutableGPTHeader backupHeader = gpt.getMutableBackupHeader();
backupHeader.setFields(newBackupHeader);
System.out.println("done.");
System.out.print(" - Setting contents of all backup table entries to match primary entries...");
MutableGPTEntry[] primaryEntries = gpt.getMutablePrimaryEntries();
MutableGPTEntry[] backupEntries = Util.arrayCopy(primaryEntries, new MutableGPTEntry[primaryEntries.length]);
gpt.setMutableBackupEntries(backupEntries);
System.out.println("done.");
// At this point, all data in gpt should have been set to intended values.
// Finalizing and checking the resulting table...
System.out.print(" - Checking if gpt.isValid() == true as it now should be...");
if(!gpt.isValid()) {
System.out.println("failed! Halting program.");
System.exit(0);
}
System.out.println("yes.");
// If we have got to this point, the table should be valid and ready to be written to disk!
System.out.println("The backup table is now ready to be written down to disk.");
System.out.print("Press enter to view the original table:");
stdin.readLine();
originalGpt.printBackupFields(System.out, "");
System.out.print("Press enter to view the modified table:");
stdin.readLine();
gpt.printBackupFields(System.out, "");
System.out.print("If you want to write this table to disk, type \"yes\" here: ");
String answer = stdin.readLine();
if(answer.equals("yes")) {
System.out.print("Getting binary data for backup table...");
byte[] newBackupGPT = gpt.getBackupTableBytes();
System.out.println("done.");
// Write the new backup GPT data to a file.
String newdataFilename2 = "gpt_backup_table-" + runTimeStamp + ".new";
System.out.print("Writing new GPT backup header and table to \"" + newdataFilename2 + "\"...");
FileOutputStream newdataFile2 = new FileOutputStream(newdataFilename2);
newdataFile2.write(newBackupGPT);
newdataFile2.close();
System.out.println("done!");
// Write to disk! Dangerous stuff...
System.out.print("Writing backup table...");
long backupTableLocation = gpt.getBackupTableBytesOffset();
int backupTableSize = newBackupGPT.length;
long targetLength = backupTableLocation + backupTableSize;
if(targetLength > llf.length()) {
System.out.println("The target storage size is reported to be");
System.out.println(llf.length() + " bytes, which is smaller than what the GPT specifies");
System.out.println("(" + (backupTableLocation+backupTableSize) + " bytes).");
System.out.println("This may be because you are accessing a block device, or an incomplete disk");
System.out.println("image. If you proceed, the disk image file, if any, may be extended, or the");
System.out.println("write will fail.");
String s = "";
while(!(s.equals("y") || s.equals("n"))) {
System.out.print("Proceed anyway (y/n)? ");
s = stdin.readLine();
}
if(s.equals("n")) {
System.exit(-1);
return;
}
else if(llf instanceof TruncatableRandomAccess) {
System.out.println("Setting length of file to: " + targetLength);
((TruncatableRandomAccess)llf).setLength(targetLength);
}
}
else if(targetLength < llf.length()) {
System.out.println("Warning: The backup table is not situated at the end of the target storage.");
String s = "";
while(!(s.equals("y") || s.equals("n"))) {
System.out.print("Proceed anyway (y/n)? ");
s = stdin.readLine();
}
if(s.equals("n")) {
System.exit(-1);
return;
}
}
System.out.print("Seeking to " + backupTableLocation + "...");
llf.seek(backupTableLocation);
System.out.println("done!");
System.out.print("Writing backup GPT data...");
llf.write(newBackupGPT);
System.out.println("done!");
// Check to see if we have succeeded.
System.out.println();
System.out.println("Checking the newly written GPT...");
GUIDPartitionTable newGpt = new GUIDPartitionTable(llf, 0);
newGpt.print(System.out, "");
if(newGpt.isValid())
System.out.println("The GPT on disk is valid!");
else {
System.out.println("INVALID GPT ON DISK! FATAL ERROR!");
System.out.println("Try to restore the original GPT tables from the backup files:");
if(backupFilename2 != null)
System.out.println(" " + backupFilename2);
System.out.println("(dd in linux can probably do the job)");
}
}
else
System.out.println("Exiting program without modifying anything.");
}
else
System.out.println("The GUID Partition Table on disk seems to be valid. No changes will be made.");
llf.close();
}
}