diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form index 1e9eac474b..dae110a36c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.form @@ -74,7 +74,7 @@ - + @@ -158,7 +158,7 @@ - + @@ -184,7 +184,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java index 86a527b60c..40582677f8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageVisualPanel1.java @@ -248,7 +248,7 @@ final class AddImageVisualPanel1 extends JPanel { typePanel.setLayout(typePanelLayout); typePanelLayout.setHorizontalGroup( typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) + .addGap(0, 544, Short.MAX_VALUE) ); typePanelLayout.setVerticalGroup( typePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -279,7 +279,7 @@ final class AddImageVisualPanel1 extends JPanel { .addComponent(typeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(typePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 77, Short.MAX_VALUE) - .addGap(0, 0, 0)) + .addContainerGap()) ); imgInfoLabel.setFont(new java.awt.Font("Tahoma", 1, 14)); // NOI18N @@ -323,7 +323,7 @@ final class AddImageVisualPanel1 extends JPanel { .addComponent(noFatOrphansCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(descLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 75, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 64, Short.MAX_VALUE) .addComponent(nextLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0)) ); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java index c4a9f6fc00..b2ce7ed1be 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardPanel3.java @@ -76,7 +76,8 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { private CurrentDirectoryFetcher fetcher; private AddImageProcess process; private AddImageAction action; - private AddContentTask addImageTask; + private AddImageTask addImageTask; + private AddLocalFilesTask addLocalFilesTask; private AddImageWizardPanel2 wizPanel; AddImageWizardPanel3(AddImageAction action, AddImageWizardPanel2 wizPanel) { @@ -165,7 +166,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { public void readSettings(WizardDescriptor settings) { JButton cancel = new JButton("Cancel"); cancel.setEnabled(false); - settings.setOptions(new Object[] {WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, cancel}); + settings.setOptions(new Object[]{WizardDescriptor.PREVIOUS_OPTION, WizardDescriptor.NEXT_OPTION, WizardDescriptor.FINISH_OPTION, cancel}); cleanupImage = null; readyToIngest = false; imgAdded = false; @@ -176,9 +177,15 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { noFatOrphans = ((Boolean) settings.getProperty(AddImageAction.NOFATORPHANS_PROP)).booleanValue(); //start the process of adding the content - addImageTask = new AddContentTask(settings); - addImageTask.execute(); - + if (dataSourceType.equals(ContentType.LOCAL) ) { + addLocalFilesTask = new AddLocalFilesTask(settings); + addLocalFilesTask.execute(); + } + else { + //disk or image + addImageTask = new AddImageTask(settings); + addImageTask.execute(); + } } /** @@ -216,14 +223,14 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { * Class for getting the currently processing directory. * */ - - private static class CurrentDirectoryFetcher extends SwingWorker { - AddContentTask task; + private static class CurrentDirectoryFetcher extends SwingWorker { + + AddImageTask task; JProgressBar prog; AddImageVisualPanel2 wiz; AddImageProcess proc; - - CurrentDirectoryFetcher(JProgressBar prog, AddImageVisualPanel2 wiz, AddImageProcess proc){ + + CurrentDirectoryFetcher(JProgressBar prog, AddImageVisualPanel2 wiz, AddImageProcess proc) { this.wiz = wiz; this.proc = proc; this.prog = prog; @@ -233,10 +240,10 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { * @return the currently processing directory */ @Override - protected Integer doInBackground(){ - try{ - while(prog.getValue() < 100 || prog.isIndeterminate()){ //TODO Rely on state variable in AddImgTask class - + protected Integer doInBackground() { + try { + while (prog.getValue() < 100 || prog.isIndeterminate()) { //TODO Rely on state variable in AddImgTask class + EventQueue.invokeLater(new Runnable() { @Override public void run() { @@ -261,13 +268,134 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { } } + /** + * Thread that will add local files to database, and then kick-off ingest + * modules. Note: the add local files task cannot currently be reverted as + * the add image task can. + * This is a separate task from AddImgTask because it is much simpler and does not require locks, + * since the underlying file manager methods acquire the locks for each transaction when adding local files. + */ + private class AddLocalFilesTask extends SwingWorker { + private JProgressBar progressBar; + private Case currentCase; + // true if the process was requested to stop + private boolean interrupted = false; + private boolean hasCritError = false; + private String errorString = null; + private WizardDescriptor settings; + private Logger logger = Logger.getLogger(AddLocalFilesTask.class.getName()); + + protected AddLocalFilesTask(WizardDescriptor settings) { + this.progressBar = wizPanel.getComponent().getCrDbProgressBar(); + currentCase = Case.getCurrentCase(); + this.settings = settings; + } + + /** + * Starts the addImage process, but does not commit the results. + * + * @return + * @throws Exception + */ + @Override + protected Integer doInBackground() { + this.setProgress(0); + // Add a cleanup task to interupt the backgroud process if the + // wizard exits while the background process is running. + AddImageAction.CleanupTask cancelledWhileRunning = action.new CleanupTask() { + @Override + void cleanup() throws Exception { + logger.log(Level.INFO, "Add local files process interrupted."); + //nothing to be cleanedup + } + }; + + cancelledWhileRunning.enable(); + try { + wizPanel.setStateStarted(); + String [] paths = dataSourcePath.split(LocalFilesPanel.FILES_SEP); + ListabsLocalPaths = new ArrayList(); + for (String path : paths) { + absLocalPaths.add(path); + } + currentCase.getServices().getFileManager().addLocalFilesDirs(absLocalPaths); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Errors occurred while running add local files. ", ex); + hasCritError = true; + errorString = ex.getMessage(); + } finally { + // process is over, doesn't need to be dealt with if cancel happens + cancelledWhileRunning.disable(); + //enqueue what would be in done() to EDT thread + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + postProcess(); + } + }); + } + return 0; + } + + /** + * + * (called by EventDispatch Thread after doInBackground finishes) + */ + protected void postProcess() { + progressBar.setIndeterminate(false); + setProgress(100); + + if (interrupted || hasCritError) { + logger.log(Level.INFO, "Handling errors or interruption that occured in local files process"); + if (hasCritError) { + //core error + wizPanel.getComponent().setErrors(errorString, true); + } + return; + } else if (errorString != null) { + //data error (non-critical) + logger.log(Level.INFO, "Handling non-critical errors that occured in local files process"); + wizPanel.getComponent().setErrors(errorString, false); + } + try { + // When everything happens without an error: + if (errorString == null) { // complete progress bar + wizPanel.getComponent().changeProgressBarTextAndColor("*Local Files added.", 100, Color.black); + } + + // Get attention for the process finish + java.awt.Toolkit.getDefaultToolkit().beep(); //BEEP! + AddImageVisualPanel2 panel = wizPanel.getComponent(); + if (panel != null) { + Window w = SwingUtilities.getWindowAncestor(panel); + if (w != null) { + w.toFront(); + } + } + + wizPanel.setStateFinished(); + + settings.putProperty(AddImageAction.IMAGEID_PROP, ""); + settings.putProperty(AddImageAction.IMAGECLEANUPTASK_PROP, null); + + // Start ingest if we can + startIngest(); + + } catch (Exception ex) { + //handle unchecked exceptions + logger.log(Level.WARNING, "Unexpected errors occurred while running post add image cleanup. ", ex); + wizPanel.getComponent().changeProgressBarTextAndColor("*Failed to add image.", 0, Color.black); // set error message + logger.log(Level.SEVERE, "Error adding image to case", ex); + } + } + } /** - * Thread that will make the JNI call to add image to database, and then kick-off - * ingest modules. + * Thread that will make the JNI call to add image to database, and then + * kick-off ingest modules. */ - private class AddContentTask extends SwingWorker { + private class AddImageTask extends SwingWorker { private JProgressBar progressBar; private Case currentCase; @@ -277,9 +405,9 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { private String errorString = null; private long start; private WizardDescriptor settings; - private Logger logger = Logger.getLogger(AddContentTask.class.getName()); + private Logger logger = Logger.getLogger(AddImageTask.class.getName()); - protected AddContentTask(WizardDescriptor settings) { + protected AddImageTask(WizardDescriptor settings) { this.progressBar = wizPanel.getComponent().getCrDbProgressBar(); currentCase = Case.getCurrentCase(); this.settings = settings; @@ -326,7 +454,6 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { return 0; } - process = currentCase.makeAddImageProcess(timeZone, true, noFatOrphans); fetcher = new CurrentDirectoryFetcher(this.progressBar, wizPanel.getComponent(), process); cancelledWhileRunning.enable(); @@ -370,9 +497,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { private void commitImage(WizardDescriptor settings) throws Exception { String contentPath = (String) settings.getProperty(AddImageAction.DATASOURCEPATH_PROP); - ContentType contentType = (ContentType) settings.getProperty(AddImageAction.DATASOURCETYPE_PROP); - //TODO check type and skip commit if local files - + String timezone = settings.getProperty(AddImageAction.TIMEZONE_PROP).toString(); settings.putProperty(AddImageAction.IMAGEID_PROP, ""); @@ -396,7 +521,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { // task cleanupImage.disable(); settings.putProperty(AddImageAction.IMAGECLEANUPTASK_PROP, null); - + logger.log(Level.INFO, "Image committed, imageId: " + imageId); logger.log(Level.INFO, PlatformUtil.getAllMemUsageInfo()); } @@ -460,7 +585,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { wizPanel.setStateFinished(); // Commit the image - if (! newContents.isEmpty()) //already commited + if (!newContents.isEmpty()) //already commited { logger.log(Level.INFO, "Assuming image already committed, will not commit."); return; @@ -489,7 +614,7 @@ class AddImageWizardPanel3 implements WizardDescriptor.Panel { wizPanel.getComponent().changeProgressBarTextAndColor("*Failed to add image.", 0, Color.black); // set error message // Log error/display warning - + logger.log(Level.SEVERE, "Error adding image to case", ex); } finally { } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index ede1498434..7f7db756c5 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -34,6 +34,7 @@ public class LocalFilesPanel extends ContentTypePanel { private List currentFiles = new ArrayList(); private boolean enableNext = false; private static LocalFilesPanel instance; + public static final String FILES_SEP = ","; /** * Creates new form LocalFilesPanel @@ -59,13 +60,15 @@ public class LocalFilesPanel extends ContentTypePanel { @Override public String getContentPaths() { + //TODO consider interface change to return list of paths instead + if (currentFiles == null) { return ""; } StringBuilder b = new StringBuilder(); for (File f : currentFiles) { b.append(f.getAbsolutePath() ); - b.append(","); + b.append(FILES_SEP); } return b.toString(); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java index 412400ba21..b57632a603 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseWizardAction.java @@ -89,6 +89,7 @@ public final class NewCaseWizardAction extends CallableSystemAction { // if the finish button is pressed (not cancelled) if (finished) { // now start the 'Add Image' wizard + //TODO fix for local AddImageAction addImageAction = SystemAction.get(AddImageAction.class); addImageAction.actionPerformed(null); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java index a7558c03d2..f4cb44e708 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/FileManager.java @@ -24,9 +24,11 @@ package org.sleuthkit.autopsy.casemodule.services; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.DerivedFile; import org.sleuthkit.datamodel.FsContent; @@ -141,11 +143,66 @@ public class FileManager implements Closeable { isFile, parentFile, rederiveDetails, toolName, toolVersion, otherDetails); } + /** + * Add local files and dirs - the general method that does it all for files + * and dirs and in bulk/ + * Sends event to inform listeners of new content (note, this is temporary, we will be using + * datamodel observer and node auto-refresh in future) + * + * @param localAbsPaths list of absolute paths to local files and dirs + * @return list of root AbstractFile objects created to represent roots of + * added local files/dirs + * @throws TskCoreException exception thrown if the object creation failed + * due to a critical system error or of the file manager has already been + * closed. There is no "revert" logic if one of the additions fails. The + * addition stops with the first error encountered. + */ + public synchronized List addLocalFilesDirs(List localAbsPaths) throws TskCoreException { + final List added = new ArrayList(); + + final List rootsToAdd = new ArrayList(); + //first validate all the inputs before any additions + for (String absPath : localAbsPaths) { + java.io.File localFile = new java.io.File(absPath); + if (!localFile.exists() || !localFile.canRead()) { + String msg = "One of the local files/dirs to add is not readable: " + localFile.getAbsolutePath() + ", aborting the process before any files added"; + logger.log(Level.SEVERE, msg); + throw new TskCoreException(msg); + } + rootsToAdd.add(localFile); + } + + for (java.io.File localRootToAdd : rootsToAdd) { + AbstractFile localFileAdded = null; + if (localRootToAdd.isFile()) { + localFileAdded = addLocalFileSingle(localRootToAdd.getAbsolutePath()); + } else { + localFileAdded = this.addLocalDir(localRootToAdd.getAbsolutePath()); + } + if (localFileAdded == null) { + String msg = "One of the local files/dirs could not be added: " + localRootToAdd.getAbsolutePath(); + logger.log(Level.SEVERE, msg); + throw new TskCoreException(msg); + } else { + added.add(localFileAdded); + } + } + + if (! added.isEmpty()) { + //TODO use datamodel observer that sends such events, and node-autorefresh + // DirectoryTreeTopComponent.getDefault().refreshContentTreeSafe(); + } + + + return added; + } + /** * Helper (internal) to add child of local dir recursively + * * @param parentVd * @param childLocalFile - * @throws TskCoreException + * @throws TskCoreException */ private void addLocalDirectoryRecInt(VirtualDirectory parentVd, java.io.File childLocalFile) throws TskCoreException { @@ -167,22 +224,23 @@ public class FileManager implements Closeable { } /** - * Add a local directory and its children recursively. - * Parent container of the local dir is added for context. - * - * Does not refresh the views of data (client must do it currently, - * will be addressed in future with node auto-refresh support) - * + * Add a local directory and its children recursively. Parent container of + * the local dir is added for context. + * + * Does not refresh the views of data (client must do it currently, will be + * addressed in future with node auto-refresh support) + * * * @param localAbsPath local absolute path of root folder whose children are - * to be added recursively. If there is a parent dir, it is added as a container, for context. + * to be added recursively. If there is a parent dir, it is added as a + * container, for context. * @return parent virtual directory folder created representing the * localAbsPath node * @throws TskCoreException exception thrown if the object creation failed * due to a critical system error or of the file manager has already been * closed, or if the localAbsPath could not be accessed */ - public synchronized VirtualDirectory addLocalDir(String localAbsPath) throws TskCoreException { + private synchronized VirtualDirectory addLocalDir(String localAbsPath) throws TskCoreException { if (tskCase == null) { throw new TskCoreException("Attempted to use FileManager after it was closed."); } @@ -198,7 +256,7 @@ public class FileManager implements Closeable { if (!localDir.isDirectory()) { throw new TskCoreException("Attempted to add a local dir that is not a directory: " + localAbsPath); } - + String parentName = null; java.io.File parentDir = localDir.getParentFile(); if (parentDir != null) { @@ -212,8 +270,7 @@ public class FileManager implements Closeable { final long localFilesRootId = tskCase.getLocalFilesRootDirectoryId(); if (parentName == null) { rootVd = tskCase.addVirtualDirectory(localFilesRootId, rootVdName); - } - else { + } else { //add parent dir for context final VirtualDirectory contextDir = tskCase.addVirtualDirectory(localFilesRootId, parentName); rootVd = tskCase.addVirtualDirectory(contextDir.getId(), rootVdName); @@ -244,7 +301,7 @@ public class FileManager implements Closeable { /** * Creates a single local file under $LocalFiles for the case, adds it to - * the database and returns it. Does not refresh the views of data. + * the database and returns it. Does not refresh the views of data. * * @param localAbsPath local absolute path of the local file, including the * file name. @@ -254,7 +311,7 @@ public class FileManager implements Closeable { * closed, or if the localAbsPath could not be accessed * */ - public synchronized LocalFile addLocalFileSingle(String localAbsPath) throws TskCoreException { + private synchronized LocalFile addLocalFileSingle(String localAbsPath) throws TskCoreException { if (tskCase == null) { throw new TskCoreException("Attempted to use FileManager after it was closed."); @@ -265,7 +322,7 @@ public class FileManager implements Closeable { /** * Creates a single local file under parentFile for the case, adds it to the - * database and returns it. Does not refresh the views of data. + * database and returns it. Does not refresh the views of data. * * @param localAbsPath local absolute path of the local file, including the * file name @@ -277,7 +334,7 @@ public class FileManager implements Closeable { * closed * */ - public synchronized LocalFile addLocalFileSingle(String localAbsPath, AbstractFile parentFile) throws TskCoreException { + private synchronized LocalFile addLocalFileSingle(String localAbsPath, AbstractFile parentFile) throws TskCoreException { if (tskCase == null) { throw new TskCoreException("Attempted to use FileManager after it was closed."); @@ -306,7 +363,7 @@ public class FileManager implements Closeable { ctime, crtime, atime, mtime, isFile, parentFile); - + return lf; }