/*-
* Copyright (C) 2006 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.tools;
import java.io.File;
import java.io.FileOutputStream;
import java.util.LinkedList;
import org.catacombae.io.ReadableFileStream;
import org.catacombae.io.ReadableRandomAccessStream;
import javax.swing.JOptionPane;
import org.catacombae.hfsexplorer.GUIUtil;
import org.catacombae.hfsexplorer.SelectDeviceDialog;
import org.catacombae.storage.io.ReadableStreamDataLocator;
import org.catacombae.storage.ps.apm.types.ApplePartitionMap;
import org.catacombae.storage.ps.gpt.types.GUIDPartitionTable;
import org.catacombae.storage.ps.mbr.types.MBRPartitionTable;
import org.catacombae.storage.io.win32.ReadableWin32FileStream;
import org.catacombae.storage.ps.Partition;
import org.catacombae.storage.ps.PartitionSystemDetector;
import org.catacombae.storage.ps.PartitionSystemHandler;
import org.catacombae.storage.ps.PartitionSystemHandlerFactory;
import org.catacombae.storage.ps.PartitionSystemType;
import org.catacombae.storage.ps.PartitionType;
import org.catacombae.storage.ps.apm.APMHandler;
import org.catacombae.storage.ps.apm.types.DriverDescriptorRecord;
import org.catacombae.storage.ps.gpt.GPTHandler;
import org.catacombae.storage.ps.mbr.MBRHandler;
public class DumpFSInfo {
public static void main(String[] args) throws Throwable {
try {
javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
/*
* Description of look&feels:
* http://java.sun.com/docs/books/tutorial/uiswing/misc/plaf.html
*/
} catch(Throwable e) {
//It's ok. Non-critical.
}
try {
dumpInfo(args);
System.exit(0);
} catch(Exception e) {
GUIUtil.displayExceptionDialog(e, 25, null);
} catch(Throwable t) {
t.printStackTrace();
throw t;
}
System.exit(1);
}
public static void dumpInfo(String[] args) throws Exception {
long runTimestamp = System.currentTimeMillis();
ReadableRandomAccessStream fsFile;
if(args.length == 1) {
if(ReadableWin32FileStream.isSystemSupported()) {
fsFile = new ReadableWin32FileStream(args[0]);
}
else {
fsFile = new ReadableFileStream(args[0]);
}
}
else if(SelectDeviceDialog.isSystemSupported()) {
if(args.length == 0) {
SelectDeviceDialog swdd =
SelectDeviceDialog.createSelectDeviceDialog(null, true,
"Select device to extract info from");
swdd.setVisible(true);
fsFile = swdd.getPartitionStream();
if(fsFile == null)
System.exit(0);
}
else {
System.out.println("Usage: java DumpFSInfo <filename>");
System.out.println(" for reading directly from a specified file, or...");
System.out.println(" java DumpFSInfo");
System.out.println(" to pop up a device dialog where " +
"you can choose which device to read");
return;
}
}
else {
System.out.println("Usage: java DumpFSInfo <filename>");
return;
}
LinkedList<File> generatedFiles = new LinkedList<File>();
long fsOffset, fsLength;
int partNum = -1;
PartitionSystemType[] detectedTypes =
PartitionSystemDetector.detectPartitionSystem(fsFile, false);
PartitionSystemType detectedType;
PartitionSystemHandler partSys;
if(detectedTypes.length == 1) {
detectedType = detectedTypes[0];
PartitionSystemHandlerFactory fact =
detectedType.createDefaultHandlerFactory();
partSys = fact.createHandler(new ReadableStreamDataLocator(fsFile));
}
else if(detectedTypes.length == 0) {
detectedType = null;
partSys = null;
}
else {
String msg = "Multiple partition system types detected:";
for(PartitionSystemType t : detectedTypes)
msg += " " + t;
throw new RuntimeException(msg);
}
if(partSys != null) {
Partition[] partitions = partSys.getPartitions();
if(partitions.length == 0) {
// Proceed to detect file system
fsOffset = 0;
try {
fsLength = fsFile.length();
} catch(Exception e) {
e.printStackTrace();
fsLength = -1;
}
}
else {
// Dump partition system to file(s)
if(partSys instanceof APMHandler) {
APMHandler apmHandler = (APMHandler) partSys;
File ddrFile = new File("fsdump-" + runTimestamp + "_ddr.dat");
FileOutputStream fos = new FileOutputStream(ddrFile);
DriverDescriptorRecord ddr =
apmHandler.readDriverDescriptorRecord();
fos.write(ddr.getData());
fos.close();
generatedFiles.add(ddrFile);
ApplePartitionMap apm = apmHandler.readPartitionMap();
if(apm == null)
throw new RuntimeException("Failed to read APM data.");
File apmFile = new File("fsdump-" + runTimestamp + "_apm.dat");
fos = new FileOutputStream(apmFile);
fos.write(apm.getData());
fos.close();
generatedFiles.add(apmFile);
}
else if(partSys instanceof GPTHandler) {
GPTHandler gptHandler = (GPTHandler) partSys;
File mbrFile = new File("fsdump-" + runTimestamp + "_protectivembr.dat");
byte[] mbrData = new byte[512];
FileOutputStream fos = new FileOutputStream(mbrFile);
fsFile.seek(0);
fsFile.readFully(mbrData);
fos.write(mbrData);
fos.close();
generatedFiles.add(mbrFile);
GUIDPartitionTable gpt = gptHandler.readPartitionTable();
if(gpt == null)
throw new RuntimeException("Failed to read GPT data.");
File gptBeginFile = new File("fsdump-" + runTimestamp + "_gptprimary.dat");
fos = new FileOutputStream(gptBeginFile);
fos.write(gpt.getPrimaryTableBytes());
fos.close();
generatedFiles.add(gptBeginFile);
File gptEndFile = new File("fsdump-" + runTimestamp + "_gptbackup.dat");
fos = new FileOutputStream(gptEndFile);
fos.write(gpt.getBackupTableBytes());
fos.close();
generatedFiles.add(gptEndFile);
}
else if(partSys instanceof MBRHandler) {
MBRHandler mbrHandler = (MBRHandler) partSys;
MBRPartitionTable mbr = mbrHandler.readPartitionTable();
File mbrFile = new File("fsdump-" + runTimestamp + "_mbr.dat");
FileOutputStream fos = new FileOutputStream(mbrFile);
fos.write(mbr.getMasterBootRecord().getBytes());
fos.close();
generatedFiles.add(mbrFile);
}
else
throw new RuntimeException("Unknown partition system type!");
Object selectedValue;
int firstPreferredPartition = 0;
for(int i = 0; i < partitions.length; ++i) {
Partition p = partitions[i];
PartitionType pt = p.getType();
if(pt == PartitionType.APPLE_HFS_CONTAINER || pt == PartitionType.APPLE_HFSX) {
firstPreferredPartition = i;
break;
}
}
selectedValue = JOptionPane.showInputDialog(null,
"Select which partition to read",
"Choose " + detectedType.getLongName() + " partition",
JOptionPane.QUESTION_MESSAGE,
null, partitions, partitions[firstPreferredPartition]);
for(int i = 0; i < partitions.length; ++i) {
if(partitions[i] == selectedValue) {
partNum = i;
break;
}
}
if(selectedValue instanceof Partition) {
Partition selectedPartition = (Partition) selectedValue;
fsOffset = selectedPartition.getStartOffset();
fsLength = selectedPartition.getLength();
}
else
throw new RuntimeException("Impossible error!");
}
}
else {
fsOffset = 0;
try {
fsLength = fsFile.length();
} catch(Exception e) {
e.printStackTrace();
fsLength = -1;
}
}
// Dump the first and last 64 KiB from the partition
byte[] buffer = new byte[65536];
File first64File;
File last64File;
if(partNum == -1) {
first64File = new File("fsdump-" + runTimestamp + "_first64.dat");
last64File = new File("fsdump-" + runTimestamp + "_last64.dat");
}
else {
first64File = new File("fsdump-" + runTimestamp + "_p" + partNum + "_first64.dat");
last64File = new File("fsdump-" + runTimestamp + "_p" + partNum + "_last64.dat");
}
if(extractDataToFile(fsFile, fsOffset, first64File, 65536))
generatedFiles.add(first64File);
long pos = fsOffset + fsLength - buffer.length;
if(pos > fsOffset &&
extractDataToFile(fsFile, pos, last64File, 65536))
generatedFiles.add(last64File);
// Display result
StringBuilder sb = new StringBuilder();
sb.append("Dumped FS info to directory:\n ");
File firstFile = generatedFiles.getFirst().getAbsoluteFile();
File firstParent = firstFile.getParentFile();
sb.append(firstParent.getAbsolutePath());
sb.append("\nThe following files were generated:\n ");
for(File f : generatedFiles)
sb.append(f.toString() + "\n ");
JOptionPane.showMessageDialog(null, sb.toString(), "Result", JOptionPane.INFORMATION_MESSAGE);
}
private static boolean extractDataToFile(ReadableRandomAccessStream fsFile, long pos, File outFile, int dataSize) {
try {
byte[] buffer = new byte[dataSize];
fsFile.seek(pos);
int bytesRead = fsFile.read(buffer);
FileOutputStream fileOut = new FileOutputStream(outFile);
fileOut.write(buffer, 0, bytesRead);
fileOut.close();
return true;
} catch(Exception e) {
e.printStackTrace();
return false;
}
}
}