/*-
* Copyright (C) 2006-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;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import org.catacombae.io.ReadableRandomAccessStream;
import org.catacombae.io.ReadableConcatenatedStream;
import org.catacombae.storage.io.win32.ReadableWin32FileStream;
import org.catacombae.hfsexplorer.gui.SelectDevicePanel;
import org.catacombae.io.ReadableFileStream;
import org.catacombae.io.RuntimeIOException;
import org.catacombae.storage.ps.Partition;
import org.catacombae.storage.fs.hfscommon.HFSCommonFileSystemRecognizer;
import org.catacombae.storage.fs.hfscommon.HFSCommonFileSystemRecognizer.FileSystemType;
import org.catacombae.storage.io.ReadableStreamDataLocator;
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.util.ObjectContainer;
public abstract class SelectDeviceDialog extends JDialog {
private SelectDevicePanel guiPanel;
// Shortcuts to variables in guiPanel
private JButton autodetectButton;
private JRadioButton selectDeviceButton;
private JRadioButton specifyDeviceNameButton;
private JButton loadButton;
private JButton cancelButton;
private JTextField specifyDeviceNameField;
private JComboBox detectedDevicesCombo;
// Additional gui stuff
private ButtonGroup selectSpecifyGroup;
private ReadableRandomAccessStream result = null;
private String resultCreatePath = null;
private String[] detectedDeviceNames;
private interface SelectDeviceDialogFactory {
public boolean isSystemSupported();
public SelectDeviceDialog createDeviceDialog(Frame owner, boolean modal,
String title);
}
private static final SelectDeviceDialogFactory factories[] = {
new WindowsFactory(),
new LinuxFactory(),
new MacOSXFactory(),
new FreeBSDFactory(),
new SolarisFactory(),
};
protected SelectDeviceDialog(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal);
setTitle(title);
this.guiPanel = new SelectDevicePanel(getExampleDeviceName());
this.autodetectButton = guiPanel.autodetectButton;
this.selectDeviceButton = guiPanel.selectDeviceButton;
this.specifyDeviceNameButton = guiPanel.specifyDeviceNameButton;
this.loadButton = guiPanel.loadButton;
this.cancelButton = guiPanel.cancelButton;
this.specifyDeviceNameField = guiPanel.specifyDeviceNameField;
this.detectedDevicesCombo = guiPanel.detectedDevicesCombo;
this.selectSpecifyGroup = new ButtonGroup();
selectSpecifyGroup.add(selectDeviceButton);
selectSpecifyGroup.add(specifyDeviceNameButton);
detectedDevicesCombo.removeAllItems();
detectedDeviceNames = detectDevices();
for(String name : detectedDeviceNames)
detectedDevicesCombo.addItem(name);
if(detectedDeviceNames.length > 0) {
detectedDevicesCombo.setSelectedIndex(0);
specifyDeviceNameField.setText(getDevicePrefix() +
detectedDevicesCombo.getSelectedItem().toString());
}
autodetectButton.addActionListener(new ActionListener() {
/* @Override */
public void actionPerformed(ActionEvent ae) {
autodetectFilesystems();
}
});
detectedDevicesCombo.addItemListener(new ItemListener() {
/* @Override */
public void itemStateChanged(ItemEvent ie) {
if(ie.getStateChange() == ItemEvent.SELECTED)
specifyDeviceNameField.setText(getDevicePrefix() +
ie.getItem().toString());
}
});
selectDeviceButton.addActionListener(new ActionListener() {
/* @Override */
public void actionPerformed(ActionEvent ae) {
detectedDevicesCombo.setEnabled(true);
specifyDeviceNameField.setEnabled(false);
specifyDeviceNameField.setText(getDevicePrefix() +
detectedDevicesCombo.getSelectedItem().toString());
}
});
specifyDeviceNameButton.addActionListener(new ActionListener() {
/* @Override */
public void actionPerformed(ActionEvent ae) {
detectedDevicesCombo.setEnabled(false);
specifyDeviceNameField.setEnabled(true);
}
});
loadButton.addActionListener(new ActionListener() {
/* @Override */
public void actionPerformed(ActionEvent ae) {
resultCreatePath = specifyDeviceNameField.getText();
result = createStream(resultCreatePath);
setVisible(false);
}
});
cancelButton.addActionListener(new ActionListener() {
/* @Override */
public void actionPerformed(ActionEvent ae) {
setVisible(false);
}
});
// JPanel container = new JPanel();
// container.setLayout(new BorderLayout());
// container.setBorder(new EmptyBorder(5, 5, 5, 5));
// container.add(guiPanel, BorderLayout.CENTER);
add(guiPanel, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setResizable(false);
}
public ReadableRandomAccessStream getPartitionStream() { return result; }
/** Could include an identifier of a partitioning scheme. This should only be used to display a descriptive locator. */
public String getPathName() { return resultCreatePath; }
protected ReadableRandomAccessStream createStream(final String path) {
return new ReadableFileStream(path);
}
protected abstract String getDevicePrefix();
protected abstract String getExampleDeviceName();
protected abstract boolean isPartition(String deviceName);
protected abstract String[] detectDevices();
public static boolean isSystemSupported() {
boolean supported = false;
for(SelectDeviceDialogFactory factory : factories) {
if(factory.isSystemSupported()) {
supported = true;
break;
}
}
return supported;
}
public static SelectDeviceDialog createSelectDeviceDialog(final Frame owner,
final boolean modal, final String title)
{
SelectDeviceDialog dialog = null;
for(SelectDeviceDialogFactory factory : factories) {
if(factory.isSystemSupported()) {
dialog = factory.createDeviceDialog(owner, modal, title);
break;
}
}
return dialog;
}
protected void autodetectFilesystems() {
LinkedList<String> plainFileSystems = new LinkedList<String>();
LinkedList<EmbeddedPartitionEntry> embeddedFileSystems = new LinkedList<EmbeddedPartitionEntry>();
//String skipPrefix = null;
// Look for file systems that sit inside partition systems unsupported by Windows.
for(int i = 0; i < detectedDeviceNames.length; ++i) {
String deviceName = detectedDeviceNames[i];
// System.out.println("Checking if \"" + name + "\" might be an unsupported partition system...");
// System.out.println(" name.startsWith(\"CdRom\") == " + name.startsWith(DEVICE_PREFIX + "CdRom"));
// System.out.println(" name.endsWith(\"Partition0\") == " + name.endsWith("Partition0"));
/*
if(name.startsWith("CdRom") ||
(name.endsWith("Partition0") &&
!(i+1 < detectedDeviceNames.length && detectedDeviceNames[i+1].endsWith("Partition1"))) ) {
*/
// We have an unidentifed partition system at "name"
// System.out.println("TRUE!");
//if(skipPrefix != null && deviceName.startsWith(skipPrefix))
// continue;
//else
// skipPrefix = null;
ReadableRandomAccessStream llf = null;
PartitionSystemHandler partSys = null;
try {
llf = createStream(getDevicePrefix() + deviceName);
PartitionSystemType[] detectedTypes =
PartitionSystemDetector.detectPartitionSystem(llf,
false);
PartitionSystemType pst;
if(detectedTypes.length == 1)
pst = detectedTypes[0];
else if(detectedTypes.length == 0)
pst = null;
else {
String msg = detectedTypes.length + " partition " +
"types detected: { ";
for(PartitionSystemType t : detectedTypes)
msg += t + " ";
msg += "} Cannot continue.";
throw new RuntimeException(msg);
}
boolean fileSystemFound = false;
if(pst != null) {
PartitionSystemHandlerFactory fact =
pst.createDefaultHandlerFactory();
partSys = fact.createHandler(
new ReadableStreamDataLocator(llf));
Partition[] parts = partSys.getPartitions();
for(int j = 0; j < parts.length; ++j) {
Partition part = parts[j];
PartitionType pt = part.getType();
if(pt == PartitionType.APPLE_HFS_CONTAINER ||
pt == PartitionType.APPLE_HFSX) {
FileSystemType fsType =
HFSCommonFileSystemRecognizer.
detectFileSystem(llf,
part.getStartOffset());
if(HFSCommonFileSystemRecognizer.isTypeSupported(
fsType)) {
fileSystemFound = true;
embeddedFileSystems.add(
new EmbeddedPartitionEntry(deviceName,
j, pst, part));
}
}
}
}
if(!fileSystemFound && !isPartition(deviceName)) {
FileSystemType fsType = HFSCommonFileSystemRecognizer.
detectFileSystem(llf, 0);
if(HFSCommonFileSystemRecognizer.isTypeSupported(fsType))
plainFileSystems.add(deviceName);
}
/* If we found file systems in embedded partition systems,
* ignore windows-detected partitions if the embedded partition
* system is at Partition0. */
//else if(deviceName.endsWith("Partition0"))
// skipPrefix = deviceName.substring(0, deviceName.length()-1);
} catch(Exception e) {
System.out.println("INFO: Non-critical exception while " +
"detecting partition system at \"" + getDevicePrefix() +
deviceName + "\": " + e.toString());
if(llf != null) {
FileSystemType fsType = HFSCommonFileSystemRecognizer.
detectFileSystem(llf, 0);
if(HFSCommonFileSystemRecognizer.isTypeSupported(fsType))
plainFileSystems.add(deviceName);
}
} finally {
if(partSys != null) {
partSys.close(); /* Will also close llf. */
}
}
}
if(plainFileSystems.size() >= 1 || embeddedFileSystems.size() >= 1) {
String[] plainStrings = plainFileSystems.toArray(
new String[plainFileSystems.size()]);
String[] embeddedStrings = new String[embeddedFileSystems.size()];
int i = 0;
for(EmbeddedPartitionEntry cur : embeddedFileSystems) {
embeddedStrings[i++] = cur.toString();
}
String[] allOptions =
new String[plainStrings.length+embeddedStrings.length];
for(i = 0; i < plainStrings.length; ++i)
allOptions[i] = plainStrings[i];
for(i = 0; i < embeddedStrings.length; ++i)
allOptions[plainStrings.length+i] = embeddedStrings[i];
Object selectedValue = JOptionPane.showInputDialog(this,
"Autodetection complete! Found " + allOptions.length + " " +
"HFS+ file systems.\n" +
"Please choose which one to load:",
"Load HFS+ file system", JOptionPane.QUESTION_MESSAGE,
null, allOptions, allOptions[0]);
if(selectedValue != null) {
int selectedIndex = -1;
for(i = 0; i < allOptions.length; ++i) {
if(selectedValue.equals(allOptions[i])) {
selectedIndex = i;
break;
}
}
if(selectedIndex == -1) {
throw new RuntimeException("selectedIndex == -1");
}
else {
if(selectedIndex >= plainStrings.length) {
// We have an embedded FS
selectedIndex -= plainStrings.length;
EmbeddedPartitionEntry embeddedInfo =
embeddedFileSystems.get(selectedIndex);
if(embeddedInfo == null)
throw new RuntimeException("embeddedInfo == null");
switch(embeddedInfo.psType) {
case APM:
case GPT:
case MBR:
ReadableRandomAccessStream llf =
createStream(getDevicePrefix() +
embeddedInfo.deviceName);
Partition p = embeddedInfo.partition;
resultCreatePath = getDevicePrefix() +
selectedValue.toString();
result = new ReadableConcatenatedStream(llf,
p.getStartOffset(), p.getLength());
setVisible(false);
break;
default:
throw new RuntimeException("Unexpected " +
"partition system: " +
embeddedInfo.psType);
}
}
else {
resultCreatePath = getDevicePrefix() +
selectedValue.toString();
result = createStream(resultCreatePath);
setVisible(false);
}
}
}
}
// else if(plainFileSystems.size() > 0) {
// int res = JOptionPane.showConfirmDialog(this, "Autodetection complete! Found an " +
// "HFS+ file system at \"" +
// plainFileSystems.getFirst() +"\".\n" +
// "Do you want to load it?",
// "Load HFS+ file system",
// JOptionPane.YES_NO_OPTION,
// JOptionPane.QUESTION_MESSAGE);
// if(res == JOptionPane.YES_OPTION) {
// result = DEVICE_PREFIX + plainFileSystems.getFirst();
// setVisible(false);
// }
// }
else
JOptionPane.showMessageDialog(this, "No HFS+ file systems found...",
"Result",
JOptionPane.INFORMATION_MESSAGE);
}
private static class WindowsFactory implements SelectDeviceDialogFactory {
public boolean isSystemSupported() {
return System.getProperty("os.name").toLowerCase().
startsWith("windows");
}
public SelectDeviceDialog createDeviceDialog(final Frame owner,
final boolean modal, final String title)
{
return new SelectDeviceDialog.Windows(owner, modal, title);
}
}
private static class Windows extends SelectDeviceDialog {
public Windows(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal, title);
}
@Override
protected ReadableRandomAccessStream createStream(final String path) {
return new ReadableWin32FileStream(path);
}
protected String getDevicePrefix() {
return "\\\\?\\GLOBALROOT\\Device\\";
}
protected String getExampleDeviceName() {
return "\\\\?\\GLOBALROOT\\Device\\Harddisk0\\Partition1";
}
protected boolean isPartition(final String deviceName) {
return !deviceName.endsWith("Partition0") &&
deviceName.matches("Harddisk[0-9]+\\\\Partition[0-9]+$");
}
/**
* This method is only tested with Windows XP (SP2, x86). Also, it won't
* work with devices that are not mounted using the Windows XP standard
* names. For example, Bo Brantén's filedisk creates a device with
* another name. However, if your file system is on a file, this method
* is not needed.
* @return a list of the names of the detected devices
*/
protected String[] detectDevices() {
LinkedList<String> activeDeviceNames = new LinkedList<String>();
/*
* Since I've been too lazy to figure out how to implement a native
* method for reading the contents of the device tree, I'll just
* make up names for at least 20 harddrives, with at least 20
* partitions in each and check for existence.
*/
/* 20 hard drives minimum... */
for(int i = 0; true; ++i) {
boolean anyFound = false;
/* 20 partitions each minimum... */
for(int j = 0; true; ++j) {
try {
/* Should I add Partition0 to the list? It really means
* "the whole drive". Partition1 is the first
* partition... */
String currentDevice =
"Harddisk" + i + "\\Partition" + j;
ReadableRandomAccessStream curFile =
createStream(getDevicePrefix() + currentDevice);
curFile.close();
activeDeviceNames.addLast(currentDevice);
anyFound = true;
} catch(Exception e) {
if(j >= 20)
break;
}
}
if(!anyFound && i >= 20)
break;
}
/* ...and 20 CD-ROMs minimum */
for(int i = 0; true; ++i) {
try {
String currentDevice = "CdRom" + i;
ReadableRandomAccessStream curFile =
createStream(getDevicePrefix() + currentDevice);
curFile.close();
activeDeviceNames.addLast(currentDevice);
}
catch(Exception e) {
if(i >= 20) {
break;
}
}
}
/* Check for TrueCrypt volumes 'A'-'Z', using their special naming
* scheme. */
for(char c = 'A'; c <= 'Z'; ++c) {
try {
String currentDevice = "TrueCryptVolume" + c;
ReadableRandomAccessStream curFile =
createStream(getDevicePrefix() + currentDevice);
curFile.close();
activeDeviceNames.addLast(currentDevice);
}
catch(Exception e) {}
}
return activeDeviceNames.toArray(
new String[activeDeviceNames.size()]);
}
}
private static abstract class CommonUNIX extends SelectDeviceDialog {
public CommonUNIX(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal, title);
}
protected abstract FilenameFilter getDiskDeviceFileNameFilter();
protected String getDevicePrefix() {
return "/dev/";
}
protected String[] detectDevices() {
final File devDirFile = new File(getDevicePrefix());
final File[] diskDevices =
devDirFile.listFiles(getDiskDeviceFileNameFilter());
final ArrayList<String> deviceNames =
new ArrayList<String>(diskDevices.length);
for(int i = 0; i < diskDevices.length; ++i) {
final String curName = diskDevices[i].getName();
final ObjectContainer<Boolean> canRead =
new ObjectContainer<Boolean>(null);
Thread t = new Thread() {
@Override
public void run() {
FileInputStream is = null;
try {
is = new FileInputStream(getDevicePrefix() +
curName);
canRead.o = true;
} catch(IOException ex) {
canRead.o = false;
} finally {
if(is != null) {
try {
is.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
}
};
t.start();
try {
/* We wait 5 seconds for the thread to finish. */
t.join(5000);
} catch(InterruptedException ex) {
throw new RuntimeException(ex);
}
t.interrupt();
if(canRead.o == null) {
System.err.println("Timeout while detecting device at: " +
getDevicePrefix() + curName);
}
else if(canRead.o) {
deviceNames.add(curName);
}
else {
/* Just ignore device if we can't open it for reading. */
}
}
String deviceNamesArray[] =
deviceNames.toArray(new String[deviceNames.size()]);
/* Sorted output is nice. */
Arrays.sort(deviceNamesArray);
return deviceNamesArray;
}
}
private static class LinuxFactory implements SelectDeviceDialogFactory {
public boolean isSystemSupported() {
return System.getProperty("os.name").toLowerCase().
startsWith("linux");
}
public SelectDeviceDialog createDeviceDialog(final Frame owner,
final boolean modal, final String title)
{
return new SelectDeviceDialog.Linux(owner, modal, title);
}
}
private static class Linux extends CommonUNIX {
private static class LinuxDeviceFilenameFilter implements FilenameFilter
{
public boolean accept(File dir, String name) {
return name.matches("sd[a-z]+([0-9]+)?$") ||
name.matches("hd[a-z]+([0-9]+)?$") ||
name.matches("sr[0-9]+$") ||
name.matches("fd[0-9]+$") ||
name.matches("dm-[0-9]+$") ||
name.matches("mmcblk[0-9]+(p[0-9]+)?$");
}
};
private static final FilenameFilter diskDeviceFileNameFilter =
new LinuxDeviceFilenameFilter();
public Linux(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal, title);
}
protected FilenameFilter getDiskDeviceFileNameFilter() {
return diskDeviceFileNameFilter;
}
protected String getExampleDeviceName() {
return "/dev/sda1";
}
protected boolean isPartition(final String deviceName) {
return deviceName.matches("[0-9]+$");
}
@Override
protected String[] detectDevices() {
/* Special case for Linux: We read /proc/partitions in order to get
* a list of all block devices. */
final LinkedList<String> deviceNames = new LinkedList<String>();
BufferedReader r = null;
try {
try {
r = new BufferedReader(new InputStreamReader(
new FileInputStream("/proc/partitions"), "UTF-8"));
} catch(IOException e) {
System.err.println("Unable to open /proc/partitions for " +
"reading! Falling back on common UNIX method for " +
"device detection.");
return super.detectDevices();
}
/* Skip first line, which is just a description. */
r.readLine();
for(String curLine; (curLine = r.readLine()) != null; ) {
if(curLine.trim().length() == 0) {
/* Ignore whitespace. */
continue;
}
/* Parse line. */
char[] curLineChars = curLine.toCharArray();
int i = 0;
/* Whitespace. */
for(; i < curLineChars.length; ++i) {
if(!Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Major device number. */
for(; i < curLineChars.length; ++i) {
if(Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Whitespace. */
for(; i < curLineChars.length; ++i) {
if(!Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Minor device number. */
for(; i < curLineChars.length; ++i) {
if(Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Whitespace. */
for(; i < curLineChars.length; ++i) {
if(!Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Block count. */
for(; i < curLineChars.length; ++i) {
if(Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Whitespace. */
for(; i < curLineChars.length; ++i) {
if(!Character.isWhitespace(curLineChars[i])) {
break;
}
}
/* Device name. */
if(i < curLineChars.length) {
final String curName =
new String(curLineChars, i,
curLineChars.length - i).trim();
try {
final FileInputStream is =
new FileInputStream(getDevicePrefix() +
curName);
is.close();
deviceNames.add(curName);
} catch(IOException ex) {
/* Just ignore device if we can't open it for
* reading. */
}
}
else {
System.err.println("Error while parsing " +
"/proc/partitions line \"" + curLine + "\".");
}
}
final String[] deviceNamesArray =
deviceNames.toArray(new String[deviceNames.size()]);
/* Sorted output is nice. */
Arrays.sort(deviceNamesArray);
return deviceNamesArray;
} catch(IOException ex) {
throw new RuntimeIOException(ex);
}
}
}
private static class MacOSXFactory implements SelectDeviceDialogFactory {
public boolean isSystemSupported() {
return System.getProperty("os.name").toLowerCase().
startsWith("mac os x");
}
public SelectDeviceDialog createDeviceDialog(final Frame owner,
final boolean modal, final String title)
{
return new SelectDeviceDialog.MacOSX(owner, modal, title);
}
}
private static class MacOSX extends CommonUNIX {
private static class MacOSXDeviceFilenameFilter
implements FilenameFilter
{
public boolean accept(File dir, String name) {
return name.matches("disk[0-9]+(s[0-9]+)?$");
}
};
private static final FilenameFilter diskDeviceFileNameFilter =
new MacOSXDeviceFilenameFilter();
public MacOSX(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal, title);
}
protected FilenameFilter getDiskDeviceFileNameFilter() {
return diskDeviceFileNameFilter;
}
protected String getExampleDeviceName() {
return "/dev/disk0s1";
}
protected boolean isPartition(final String deviceName) {
return deviceName.matches("s[0-9]+$");
}
}
private static class FreeBSDFactory implements SelectDeviceDialogFactory {
public boolean isSystemSupported() {
return System.getProperty("os.name").toLowerCase().
startsWith("freebsd");
}
public SelectDeviceDialog createDeviceDialog(final Frame owner,
final boolean modal, final String title)
{
return new SelectDeviceDialog.FreeBSD(owner, modal, title);
}
}
private static class FreeBSD extends CommonUNIX {
private static class FreeBSDDeviceFilenameFilter
implements FilenameFilter
{
public String[] knownDevices;
public FreeBSDDeviceFilenameFilter(String[] knownDevices) {
this.knownDevices = knownDevices;
}
public boolean accept(File dir, String name) {
if(knownDevices != null) {
return acceptSpecific(dir, name);
}
else {
return acceptGeneric(dir, name);
}
}
private boolean acceptSpecific(File dir, String name) {
boolean acceptResult = false;
for(String device : knownDevices) {
if(name.matches("^" + device + "+([sp][0-9]+([a-z]+)?)?$"))
{
acceptResult = true;
break;
}
}
return acceptResult;
}
private boolean acceptGeneric(File dir, String name) {
/* Device naming info retrieved 2014-08-22 from:
* https://www.freebsd.org/doc/handbook/disk-organization.html
*/
return name.matches("ada[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("ad[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("da[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("cd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("acd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("fd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("mcd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("scd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("sa[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("ast[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("aacd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("mlxd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("mlyd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("amrd[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("idad[0-9]+([sp][0-9]+([a-z]+)?)?$") ||
name.matches("twed[0-9]+([sp][0-9]+([a-z]+)?)?$");
}
};
private static final FilenameFilter genericdiskDeviceFileNameFilter =
new FreeBSDDeviceFilenameFilter(null);
public FreeBSD(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal, title);
}
protected FilenameFilter getDiskDeviceFileNameFilter() {
String[] devices = null;
Process sysctlProcess = null;
BufferedReader sysctlStdoutReader = null;
try {
sysctlProcess = Runtime.getRuntime().exec(
new String[] { "/sbin/sysctl", "kern.disks" });
sysctlStdoutReader =
new BufferedReader(new InputStreamReader(
sysctlProcess.getInputStream(), "UTF-8"));
String disksString = sysctlStdoutReader.readLine();
int retval = sysctlProcess.waitFor();
if(retval != 0) {
System.err.println("sysctl returned error value (" +
retval + "). Falling back on exhaustive " +
"detection method.");
}
else if(disksString.startsWith("kern.disks: ")) {
devices = disksString.substring("kern.disks: ".length()).
split("\\s");
if(devices.length == 0) {
/* We should definitely have at least one disk. This
* must be an error. */
System.err.println("No disks returned from sysctl. " +
"Falling back on exhaustive detection " +
"method...");
devices = null;
}
}
else {
System.err.println("Unexpected output from sysctl " +
"command: \"" + disksString + "\" Falling back " +
"on exhaustive detection method...");
}
} catch(IOException ex) {
System.err.println("Exception while issuing sysctl command:");
ex.printStackTrace();
System.err.println("Falling back on exhaustive detection " +
"method...");
} catch(InterruptedException e) {
throw new RuntimeException(e);
} finally {
if(sysctlStdoutReader != null) {
try {
sysctlStdoutReader.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
FilenameFilter filter;
if(devices != null) {
filter = new FreeBSDDeviceFilenameFilter(devices);
}
else {
filter = genericdiskDeviceFileNameFilter;
}
return filter;
}
protected String getExampleDeviceName() {
return "/dev/da0";
}
protected boolean isPartition(final String deviceName) {
return deviceName.matches("s[0-9]+$");
}
}
private static class SolarisFactory implements SelectDeviceDialogFactory {
public boolean isSystemSupported() {
return System.getProperty("os.name").toLowerCase().
startsWith("sunos");
}
public SelectDeviceDialog createDeviceDialog(final Frame owner,
final boolean modal, final String title)
{
return new SelectDeviceDialog.Solaris(owner, modal, title);
}
}
private static class Solaris extends CommonUNIX {
private static class SolarisDeviceFilenameFilter
implements FilenameFilter
{
public boolean accept(File dir, String name) {
/* All the files in /dev/dsk should be valid block device names.
* Alternatively we could use the regexp:
* c[0-9]+(t[0-9]+)?d[0-9]+([ps][0-9]+)?$
*/
return true;
}
};
private static final FilenameFilter diskDeviceFileNameFilter =
new SolarisDeviceFilenameFilter();
public Solaris(final Frame owner, final boolean modal,
final String title)
{
super(owner, modal, title);
}
protected FilenameFilter getDiskDeviceFileNameFilter() {
return diskDeviceFileNameFilter;
}
@Override
protected String getDevicePrefix() {
return "/dev/dsk/";
}
protected String getExampleDeviceName() {
return "/dev/dsk/c0t0d0s0";
}
protected boolean isPartition(final String deviceName) {
return deviceName.matches("[ps][0-9]+$");
}
}
private static final class EmbeddedPartitionEntry {
public final String deviceName;
public final long partitionNumber;
public final PartitionSystemType psType;
public final Partition partition;
public EmbeddedPartitionEntry(String deviceName, long partitionNumber,
PartitionSystemType psType, Partition partition) {
this.deviceName = deviceName;
this.partitionNumber = partitionNumber;
this.psType = psType;
this.partition = partition;
}
private String getPartitionSystemString() {
switch(psType) {
case MBR:
return "MBR";
case GPT:
return "GPT";
case APM:
return "APM";
case DOS_EXTENDED:
return "EBR";
default:
return "Unknown partition system";
}
}
@Override
public String toString() {
return deviceName + "[" + getPartitionSystemString() + ":Partition" +
partitionNumber + "]";
}
}
}