diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java index e4d2b89b17..52c1e55ecc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddLogicalImageTask.java @@ -30,7 +30,6 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.imagewriter.ImageWriterSettings; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -40,7 +39,7 @@ import org.sleuthkit.datamodel.TskCoreException; * - add alert.txt and users.txt files to report * - add an image data source to the case database. */ -public class AddLogicalImageTask extends AddImageTask { +public class AddLogicalImageTask extends AddMultipleImageTask { private final static Logger logger = Logger.getLogger(AddLogicalImageTask.class.getName()); private final static String ALERT_TXT = "alert.txt"; //NON-NLS @@ -50,16 +49,14 @@ public class AddLogicalImageTask extends AddImageTask { private final DataSourceProcessorCallback callback; private final DataSourceProcessorProgressMonitor progressMonitor; - public AddLogicalImageTask(String deviceId, String imagePath, int sectorSize, - String timeZone, boolean ignoreFatOrphanFiles, - String md5, String sha1, String sha256, - ImageWriterSettings imageWriterSettings, + public AddLogicalImageTask(String deviceId, + List imagePaths, + String timeZone, File src, File dest, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback - ) { - super(deviceId, imagePath, sectorSize, timeZone, ignoreFatOrphanFiles, - md5, sha1, sha256, imageWriterSettings, progressMonitor, callback); + ) throws NoCurrentCaseException { + super(deviceId, imagePaths, timeZone, progressMonitor, callback); this.src = src; this.dest = dest; this.progressMonitor = progressMonitor; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddMultipleImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddMultipleImageTask.java new file mode 100644 index 0000000000..03ca4ef5bb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddMultipleImageTask.java @@ -0,0 +1,252 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Image; +import org.sleuthkit.datamodel.LocalFilesDataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.SleuthkitJNI; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; + +/** + * + * A runnable that adds multiple images to the case database + * + */ +@Messages({ + "AddMultipleImageTask.fsTypeUnknownErr=Cannot determine file system type" +}) +class AddMultipleImageTask implements Runnable { + + private static final Logger LOGGER = Logger.getLogger(AddMultipleImageTask.class.getName()); + public static final String TSK_FS_TYPE_UNKNOWN_ERR_MSG = Bundle.AddMultipleImageTask_fsTypeUnknownErr(); + private final String deviceId; + private final List imageFilePaths; + private final String timeZone; + private final DataSourceProcessorProgressMonitor progressMonitor; + private final DataSourceProcessorCallback callback; + private final Case currentCase; + private boolean criticalErrorOccurred; + private volatile boolean cancelled; + + /** + * Constructs a runnable that adds multiple image files + * to a case database. If Sleuth Kit fails to find a filesystem + * in any of input image files, the file is added to the case as a + * local/logical file instead. + * + * @param deviceId An ASCII-printable identifier for the device + * associated with the data source that is intended + * to be unique across multiple cases (e.g., a UUID). + * @param imageFilePaths The paths of the multiple output files. + * @param timeZone The time zone to use when processing dates and + * times for the image, obtained from + * java.util.TimeZone.getID. + * @param progressMonitor Progress monitor for reporting progress during + * processing. + * @param callback Callback to call when processing is done. + * @throws NoCurrentCaseException The exception if there is no open case. + */ + @Messages({ + "# {0} - file", "AddMultipleImageTask.addingFileAsLogicalFile=Adding: {0} as logical file", + "# {0} - deviceId", "# {1} - exceptionMessage", + "AddMultipleImageTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device %s: %s", + }) + AddMultipleImageTask(String deviceId, List imageFilePaths, String timeZone, + DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) throws NoCurrentCaseException { + this.deviceId = deviceId; + this.imageFilePaths = imageFilePaths; + this.timeZone = timeZone; + this.callback = callback; + this.progressMonitor = progressMonitor; + currentCase = Case.getCurrentCaseThrows(); + } + + @Override + public void run() { + /* + * Try to add the input image files as images. + */ + List newDataSources = new ArrayList<>(); + List localFileDataSourcePaths = new ArrayList<>(); + List errorMessages = new ArrayList<>(); + currentCase.getSleuthkitCase().acquireSingleUserCaseWriteLock(); + try { + progressMonitor.setIndeterminate(true); + for (String imageFilePath : imageFilePaths) { + if (!cancelled) { + addImageToCase(imageFilePath, newDataSources, localFileDataSourcePaths, errorMessages); + } + } + } finally { + currentCase.getSleuthkitCase().releaseSingleUserCaseWriteLock(); + } + + /* + * Try to add any input image files that did not have file systems as a + * single local/logical files set with the device id as the root virtual + * directory name. + */ + if (!cancelled && !localFileDataSourcePaths.isEmpty()) { + FileManager fileManager = currentCase.getServices().getFileManager(); + FileManager.FileAddProgressUpdater progressUpdater = (final AbstractFile newFile) -> { + progressMonitor.setProgressText(Bundle.AddMultipleImageTask_addingFileAsLogicalFile(Paths.get(newFile.getParentPath(), newFile.getName()))); + }; + try { + LocalFilesDataSource localFilesDataSource = fileManager.addLocalFilesDataSource(deviceId, "", timeZone, localFileDataSourcePaths, progressUpdater); + newDataSources.add(localFilesDataSource); + } catch (TskCoreException | TskDataException ex) { + errorMessages.add(Bundle.AddMultipleImageTask_errorAddingImgWithoutFileSystem(deviceId, ex.getLocalizedMessage())); + criticalErrorOccurred = true; + } + } + + /* + * This appears to be the best that can be done to indicate completion + * with the DataSourceProcessorProgressMonitor in its current form. + */ + progressMonitor.setProgress(0); + progressMonitor.setProgress(100); + + /* + * Pass the results back via the callback. + */ + DataSourceProcessorResult result; + if (criticalErrorOccurred) { + result = DataSourceProcessorResult.CRITICAL_ERRORS; + } else if (!errorMessages.isEmpty()) { + result = DataSourceProcessorResult.NONCRITICAL_ERRORS; + } else { + result = DataSourceProcessorResult.NO_ERRORS; + } + callback.done(result, errorMessages, newDataSources); + criticalErrorOccurred = false; + } + + /** + * Attempts to cancel the processing of the input image files. May result in + * partial processing of the input. + */ + public void cancelTask() { + LOGGER.log(Level.WARNING, "AddMultipleImageTask cancelled, processing may be incomplete"); // NON-NLS + cancelled = true; + } + + /** + * Attempts to add an input image to the case. + * + * @param imageFilePath The image file path. + * @param newDataSources If the image is added, a data source is + * added to this list for eventual return to + * the caller via the callback. + * @param localFileDataSourcePaths If the image cannot be added because + * Sleuth Kit cannot detect a filesystem, the + * image file path is added to this list for + * later addition as a part of a + * local/logical files data source. + * @param errorMessages If there are any error messages, the + * error messages are added to this list for + * eventual return to the caller via the + * callback. + */ + @Messages({ + "# {0} - imageFilePath", "AddMultipleImageTask.adding=Adding: {0}", + "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2}", + "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2}", + "# {0} - imageFilePath", "# {1} - deviceId", "# {2} - exceptionMessage", "AddMultipleImageTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2}", + }) + private void addImageToCase(String imageFilePath, List newDataSources, List localFileDataSourcePaths, List errorMessages) { + /* + * Try to add the image to the case database as a data source. + */ + progressMonitor.setProgressText(Bundle.AddMultipleImageTask_adding(imageFilePath)); + SleuthkitCase caseDatabase = currentCase.getSleuthkitCase(); + SleuthkitJNI.CaseDbHandle.AddImageProcess addImageProcess = caseDatabase.makeAddImageProcess(timeZone, false, false, ""); + try { + addImageProcess.run(deviceId, new String[]{imageFilePath}); + } catch (TskCoreException ex) { + if (ex.getMessage().contains(TSK_FS_TYPE_UNKNOWN_ERR_MSG)) { + /* + * If Sleuth Kit failed to add the image because it did not find + * a file system, save the image path so it can be added to the + * case as part of a local/logical files data source. All other + * errors are critical. + */ + localFileDataSourcePaths.add(imageFilePath); + } else { + errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage())); + criticalErrorOccurred = true; + } + /* + * Either way, the add image process needs to be reverted. + */ + try { + addImageProcess.revert(); + } catch (TskCoreException e) { + errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorReverting(imageFilePath, deviceId, e.getLocalizedMessage())); + criticalErrorOccurred = true; + } + return; + } catch (TskDataException ex) { + errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage())); + } + + /* + * Try to commit the results of the add image process, retrieve the new + * image from the case database, and add it to the list of new data + * sources to be returned via the callback. + */ + try { + long imageId = addImageProcess.commit(); + Image dataSource = caseDatabase.getImageById(imageId); + newDataSources.add(dataSource); + + /* + * Verify the size of the new image. Note that it may not be what is + * expected, but at least part of it was added to the case. + */ + String verificationError = dataSource.verifyImageSize(); + if (!verificationError.isEmpty()) { + errorMessages.add(Bundle.AddMultipleImageTask_nonCriticalErrorAdding(imageFilePath, deviceId, verificationError)); + } + } catch (TskCoreException ex) { + /* + * The add image process commit failed or querying the case database + * for the newly added image failed. Either way, this is a critical + * error. + */ + errorMessages.add(Bundle.AddMultipleImageTask_criticalErrorAdding(imageFilePath, deviceId, ex.getLocalizedMessage())); + criticalErrorOccurred = true; + } + } + +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 1a55b465df..6be0cbce84 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -14,6 +14,26 @@ AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1} # {0} - src # {1} - dest AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1} +# {0} - imageFilePath +AddMultipleImageTask.adding=Adding: {0} +# {0} - file +AddMultipleImageTask.addingFileAsLogicalFile=Adding: {0} as logical file +# {0} - imageFilePath +# {1} - deviceId +# {2} - exceptionMessage +AddMultipleImageTask.criticalErrorAdding=Critical error adding {0} for device {1}: {2} +# {0} - imageFilePath +# {1} - deviceId +# {2} - exceptionMessage +AddMultipleImageTask.criticalErrorReverting=Critical error reverting add image process for {0} for device {1}: {2} +# {0} - deviceId +# {1} - exceptionMessage +AddMultipleImageTask.errorAddingImgWithoutFileSystem=Error adding images without file systems for device %s: %s +AddMultipleImageTask.fsTypeUnknownErr=Cannot determine file system type +# {0} - imageFilePath +# {1} - deviceId +# {2} - exceptionMessage +AddMultipleImageTask.nonCriticalErrorAdding=Non-critical error adding {0} for device {1}: {2} # {0} - exception message Case.closeException.couldNotCloseCase=Error closing case: {0} Case.creationException.couldNotAcquireDirLock=Failed to get lock on case directory @@ -187,19 +207,21 @@ LogicalImagerDSProcessor.dataSourceType=Autopsy Imager LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists # {0} - directory LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0} +# {0} - file +LogicalImagerDSProcessor.failToGetCanonicalPath=Fail to get canonical path for {0} # {0} - imageDirPath LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected. +LogicalImagerDSProcessor.noCurrentCase=No current case LogicalImagerPanel.imageTable.columnModel.title0=Hostname LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date # {0} - sparseImageDirectory -# {1} - image -LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1} +LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain any images # {0} - invalidFormatDirectory LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found LogicalImagerPanel.messageLabel.noImageSelected=No image selected -LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ... +LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for images ... LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive Menu/Case/OpenRecentCase=Open Recent Case CTL_CaseDeleteAction=Delete Case diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java index b817b77b21..bee70bfd2a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerDSProcessor.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule; import java.io.File; +import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -46,7 +47,6 @@ import org.sleuthkit.datamodel.Content; public class LogicalImagerDSProcessor implements DataSourceProcessor { private static final String LOGICAL_IMAGER_DIR = "LogicalImager"; //NON-NLS - private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private final LogicalImagerPanel configPanel; private AddLogicalImageTask addLogicalImageTask; @@ -128,6 +128,8 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { "# {0} - imageDirPath", "LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.", "# {0} - directory", "LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}", "# {0} - directory", "LogicalImagerDSProcessor.directoryAlreadyExists=Directory {0} already exists", + "# {0} - file", "LogicalImagerDSProcessor.failToGetCanonicalPath=Fail to get canonical path for {0}", + "LogicalImagerDSProcessor.noCurrentCase=No current case", }) @Override public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { @@ -170,9 +172,31 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { String deviceId = UUID.randomUUID().toString(); String timeZone = Calendar.getInstance().getTimeZone().getID(); boolean ignoreFatOrphanFiles = false; - run(deviceId, Paths.get(src.toString(), SPARSE_IMAGE_VHD).toString(), 0, - timeZone, ignoreFatOrphanFiles, null, null, null, src, dest, - progressMonitor, callback); + + // Get all VHD files in the src directory + List imagePaths = new ArrayList<>(); + for (File f : src.listFiles()) { + if (f.getName().endsWith(".vhd")) { + try { + imagePaths.add(f.getCanonicalPath()); + } catch (IOException ex) { + String msg = Bundle.LogicalImagerDSProcessor_failToGetCanonicalPath(f.getName()); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } + } + } + try { + run(deviceId, imagePaths, + timeZone, src, dest, + progressMonitor, callback); + } catch (NoCurrentCaseException ex) { + String msg = Bundle.LogicalImagerDSProcessor_noCurrentCase(); + errorList.add(msg); + callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources); + return; + } } /** @@ -186,29 +210,21 @@ public class LogicalImagerDSProcessor implements DataSourceProcessor { * associated with the data source that is * intended to be unique across multiple cases * (e.g., a UUID). - * @param imagePath Path to the image file. - * @param sectorSize The sector size (use '0' for autodetect). + * @param imagePaths Paths to the image files. * @param timeZone The time zone to use when processing dates * and times for the image, obtained from * java.util.TimeZone.getID. - * @param ignoreFatOrphanFiles Whether to parse orphans if the image has a - * FAT filesystem. - * @param md5 The MD5 hash of the image, may be null. - * @param sha1 The SHA-1 hash of the image, may be null. - * @param sha256 The SHA-256 hash of the image, may be null. * @param src The source directory of image. * @param dest The destination directory to copy the source. * @param progressMonitor Progress monitor for reporting progress * during processing. * @param callback Callback to call when processing is done. */ - private void run(String deviceId, String imagePath, int sectorSize, String timeZone, - boolean ignoreFatOrphanFiles, String md5, String sha1, String sha256, + private void run(String deviceId, List imagePaths, String timeZone, File src, File dest, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback - ) { - addLogicalImageTask = new AddLogicalImageTask(deviceId, imagePath, sectorSize, - timeZone, ignoreFatOrphanFiles, md5, sha1, sha256, null, src, dest, + ) throws NoCurrentCaseException { + addLogicalImageTask = new AddLogicalImageTask(deviceId, imagePaths, timeZone, src, dest, progressMonitor, callback); new Thread(addLogicalImageTask).start(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java index 492cbb7d3a..37291a031b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Color; import java.awt.Component; import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.FileStore; import java.nio.file.Files; @@ -54,7 +55,6 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; public class LogicalImagerPanel extends JPanel implements DocumentListener { private static final long serialVersionUID = 1L; - private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS private static final String NO_IMAGE_SELECTED = Bundle.LogicalImagerPanel_messageLabel_noImageSelected(); private static final String DRIVE_HAS_NO_IMAGES = Bundle.LogicalImagerPanel_messageLabel_driveHasNoImages(); private static final String[] EMPTY_LIST_DATA = {}; @@ -302,8 +302,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { @Messages({ "# {0} - sparseImageDirectory", - "# {1} - image", - "LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}", + "LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain any images", "# {0} - invalidFormatDirectory", "LogicalImagerPanel.messageLabel.directoryFormatInvalid=Directory {0} does not match format Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS" }) @@ -317,9 +316,15 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { String path = fileChooser.getSelectedFile().getPath(); Matcher m = regex.matcher(path); if (m.find()) { - Path vhdPath = Paths.get(path, SPARSE_IMAGE_VHD); - if (!vhdPath.toFile().exists()) { - setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path, SPARSE_IMAGE_VHD)); + File dir = Paths.get(path).toFile(); + String[] vhdFiles = dir.list(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".vhd"); + } + }); + if (vhdFiles.length == 0) { + setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryDoesNotContainSparseImage(path)); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); return; } @@ -348,6 +353,16 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } } + private boolean dirHasVhdFiles(File dir) { + File[] fList = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(".vhd"); + } + }); + return (fList != null && fList.length != 0); + } + private void driveListSelect() { String selectedStr = driveList.getSelectedValue(); if (selectedStr == null) { @@ -361,10 +376,9 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { imageTableModel = new ImageTableModel(); int row = 0; // Find all directories with name like Logical_Imager_HOSTNAME_yyyymmdd_HH_MM_SS - // and has a sparse_image.vhd file in it + // and has vhd files in it for (File file : fList) { - if (file.isDirectory() - && Paths.get(driveLetter, file.getName(), SPARSE_IMAGE_VHD).toFile().exists()) { + if (file.isDirectory() && dirHasVhdFiles(file)) { String dir = file.getName(); Matcher m = regex.matcher(dir); if (m.find()) { @@ -431,7 +445,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { private void toggleMouseAndKeyListeners(Component component, boolean isEnable) { component.setEnabled(isEnable); } - + private void manualRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manualRadioButtonActionPerformed browseButton.setEnabled(true); @@ -442,7 +456,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { toggleMouseAndKeyListeners(imageTable, false); refreshButton.setEnabled(false); - + choosenImageDirPath = null; setNormalMessage(""); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); @@ -450,25 +464,25 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { private void importRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importRadioButtonActionPerformed browseButton.setEnabled(false); - + toggleMouseAndKeyListeners(driveList, true); toggleMouseAndKeyListeners(driveListScrollPane, true); toggleMouseAndKeyListeners(imageScrollPane, true); toggleMouseAndKeyListeners(imageTable, true); refreshButton.setEnabled(true); - + choosenImageDirPath = null; setNormalMessage(""); refreshButton.doClick(); }//GEN-LAST:event_importRadioButtonActionPerformed @Messages({ - "LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...", + "LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for images ...", "LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found" }) private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed - // Scan external drives for sparse_image.vhd + // Scan external drives for vhd images clearImageTable(); setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_scanningExternalDrives()); List listData = new ArrayList<>(); @@ -530,7 +544,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } }//GEN-LAST:event_driveListMouseReleased - + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browseButton; private javax.swing.ButtonGroup buttonGroup1; @@ -573,7 +587,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { Path getImageDirPath() { return choosenImageDirPath; } - + @Override public void insertUpdate(DocumentEvent e) { }