19 package org.sleuthkit.autopsy.logicalimager.dsp;
 
   21 import java.io.BufferedReader;
 
   23 import java.io.FileInputStream;
 
   24 import java.io.FilenameFilter;
 
   25 import java.io.IOException;
 
   26 import java.io.InputStreamReader;
 
   27 import java.nio.file.Files;
 
   28 import java.nio.file.Path;
 
   29 import java.nio.file.Paths;
 
   30 import java.util.ArrayList;
 
   31 import java.util.Arrays;
 
   32 import java.util.HashMap;
 
   33 import java.util.Iterator;
 
   34 import java.util.List;
 
   36 import java.util.logging.Level;
 
   37 import javax.annotation.concurrent.GuardedBy;
 
   38 import org.apache.commons.io.FileUtils;
 
   39 import org.openide.util.NbBundle.Messages;
 
   62 final class AddLogicalImageTask 
implements Runnable {
 
   79         FileId(
long dataSourceId, 
long fileId) {
 
   89         long getDataSourceId() {
 
  103     private final static Logger LOGGER = Logger.getLogger(AddLogicalImageTask.class.getName());
 
  104     private final static String SEARCH_RESULTS_TXT = 
"SearchResults.txt"; 
 
  105     private final static String USERS_TXT = 
"_users.txt"; 
 
  106     private final static String MODULE_NAME = 
"Logical Imager"; 
 
  107     private final static String ROOT_STR = 
"root"; 
 
  108     private final static String VHD_EXTENSION = 
".vhd"; 
 
  109     private final static int REPORT_PROGRESS_INTERVAL = 100;
 
  110     private final static int POST_ARTIFACT_INTERVAL = 1000;
 
  111     private final String deviceId;
 
  112     private final String timeZone;
 
  113     private final File src;
 
  114     private final File dest;
 
  115     private final Host host;
 
  116     private final DataSourceProcessorCallback callback;
 
  117     private final DataSourceProcessorProgressMonitor progressMonitor;
 
  118     private final Blackboard blackboard;
 
  119     private final Case currentCase;
 
  121     private volatile boolean cancelled;
 
  122     private volatile boolean createVHD;
 
  123     private long totalFiles;
 
  124     private Map<String, Long> imagePathToObjIdMap;
 
  126     private final Object addMultipleImagesLock;
 
  127     @GuardedBy(
"addMultipleImagesLock")
 
  128     private AddMultipleImagesTask addMultipleImagesTask = null;
 
  130     AddLogicalImageTask(String deviceId,
 
  132             File src, File dest, Host host,
 
  133             DataSourceProcessorProgressMonitor progressMonitor,
 
  134             DataSourceProcessorCallback callback
 
  135     ) throws NoCurrentCaseException {
 
  136         this.deviceId = deviceId;
 
  137         this.timeZone = timeZone;
 
  141         this.progressMonitor = progressMonitor;
 
  142         this.callback = callback;
 
  143         this.currentCase = Case.getCurrentCase();
 
  144         this.blackboard = this.currentCase.getServices().getArtifactsBlackboard();
 
  145         this.addMultipleImagesLock = 
new Object();
 
  153         "# {0} - src", 
"# {1} - dest", 
"AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}",
 
  154         "AddLogicalImageTask.doneCopying=Done copying",
 
  155         "# {0} - src", 
"# {1} - dest", 
"AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}",
 
  156         "# {0} - file", 
"AddLogicalImageTask.addingToReport=Adding {0} to report",
 
  157         "# {0} - file", 
"AddLogicalImageTask.doneAddingToReport=Done adding {0} to report",
 
  158         "AddLogicalImageTask.ingestionCancelled=Ingestion cancelled",
 
  159         "# {0} - file", 
"AddLogicalImageTask.failToGetCanonicalPath=Fail to get canonical path for {0}",
 
  160         "# {0} - sparseImageDirectory", 
"AddLogicalImageTask.directoryDoesNotContainSparseImage=Directory {0} does not contain any images",
 
  161         "AddLogicalImageTask.noCurrentCase=No current case",
 
  162         "AddLogicalImageTask.addingInterestingFiles=Adding search results as interesting files",
 
  163         "AddLogicalImageTask.doneAddingInterestingFiles=Done adding search results as interesting files",
 
  164         "# {0} - SearchResults.txt", 
"# {1} - directory", 
"AddLogicalImageTask.cannotFindFiles=Cannot find {0} in {1}",
 
  165         "# {0} - reason", 
"AddLogicalImageTask.failedToAddInterestingFiles=Failed to add interesting files: {0}",
 
  166         "AddLogicalImageTask.addingExtractedFiles=Adding extracted files",
 
  167         "AddLogicalImageTask.doneAddingExtractedFiles=Done adding extracted files",
 
  168         "# {0} - reason", 
"AddLogicalImageTask.failedToGetTotalFilesCount=Failed to get total files count: {0}",
 
  169         "AddLogicalImageTask.addImageCancelled=Add image cancelled" 
  173         List<String> errorList = 
new ArrayList<>();
 
  174         List<Content> emptyDataSources = 
new ArrayList<>();
 
  177             progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString()));
 
  178             FileUtils.copyDirectory(src, dest);
 
  179             progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying());
 
  180         } 
catch (IOException ex) {
 
  182             String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString());
 
  189             deleteDestinationDirectory();
 
  190             errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
 
  191             callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  196         String resultsFilename;
 
  197         if (Paths.get(dest.toString(), SEARCH_RESULTS_TXT).toFile().exists()) {
 
  198             resultsFilename = SEARCH_RESULTS_TXT;
 
  200             errorList.add(Bundle.AddLogicalImageTask_cannotFindFiles(SEARCH_RESULTS_TXT, dest.toString()));
 
  201             callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  205         progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(resultsFilename));
 
  206         String status = addReport(Paths.get(dest.toString(), resultsFilename), resultsFilename + 
" " + src.getName());
 
  207         if (status != null) {
 
  208             errorList.add(status);
 
  209             callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  212         progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(resultsFilename));
 
  215         File[] userFiles = dest.listFiles(
new FilenameFilter() {
 
  217             public boolean accept(File dir, String name) {
 
  218                 return name.endsWith(USERS_TXT);
 
  222         for (File userFile : userFiles) {
 
  223             progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(userFile.getName()));
 
  224             status = addReport(userFile.toPath(), userFile.getName() + 
" " + src.getName());
 
  225             if (status != null) {
 
  226                 errorList.add(status);
 
  227                 callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  230             progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(userFile.getName()));
 
  234         List<String> imagePaths = 
new ArrayList<>();
 
  235         for (File f : dest.listFiles()) {
 
  236             if (f.getName().endsWith(VHD_EXTENSION)) {
 
  238                     imagePaths.add(f.getCanonicalPath());
 
  239                 } 
catch (IOException ioe) {
 
  240                     String msg = Bundle.AddLogicalImageTask_failToGetCanonicalPath(f.getName());
 
  242                     callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  248         Path resultsPath = Paths.get(dest.toString(), resultsFilename);
 
  250             totalFiles = Files.lines(resultsPath).count() - 1; 
 
  251         } 
catch (IOException ex) {
 
  252             errorList.add(Bundle.AddLogicalImageTask_failedToGetTotalFilesCount(ex.getMessage()));
 
  253             callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  257         List<Content> newDataSources = 
new ArrayList<>();
 
  258         Map<String, List<FileId>> interestingFileMap = 
new HashMap<>();
 
  260         if (imagePaths.isEmpty()) {
 
  263             File root = Paths.get(dest.toString(), ROOT_STR).toFile();
 
  264             if (root.exists() && root.isDirectory()) {
 
  265                 imagePaths.add(root.getAbsolutePath());
 
  267                 String msg = Bundle.AddLogicalImageTask_directoryDoesNotContainSparseImage(dest);
 
  269                 callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  274                 progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles());
 
  275                 interestingFileMap = addExtractedFiles(dest, resultsPath, host, newDataSources);
 
  276                 progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles());
 
  277             } 
catch (IOException | TskCoreException ex) {
 
  278                 errorList.add(ex.getMessage());
 
  279                 LOGGER.log(Level.SEVERE, String.format(
"Failed to add datasource: %s", ex.getMessage()), ex); 
 
  280                 callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  287                 synchronized (addMultipleImagesLock) {
 
  289                         LOGGER.log(Level.SEVERE, 
"Add VHD cancelled"); 
 
  290                         errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
 
  291                         callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  294                     addMultipleImagesTask = 
new AddMultipleImagesTask(deviceId, imagePaths, timeZone, host, progressMonitor);
 
  296                 addMultipleImagesTask.run();
 
  297                 if (addMultipleImagesTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
 
  298                     LOGGER.log(Level.SEVERE, 
"Failed to add VHD datasource"); 
 
  299                     callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, addMultipleImagesTask.getErrorMessages(), emptyDataSources);
 
  303                     interestingFileMap = getInterestingFileMapForVHD(Paths.get(dest.toString(), resultsFilename));
 
  304                 } 
catch (TskCoreException | IOException ex) {
 
  305                     errorList.add(Bundle.AddLogicalImageTask_failedToAddInterestingFiles(ex.getMessage()));
 
  306                     LOGGER.log(Level.SEVERE, 
"Failed to add interesting files", ex); 
 
  307                     callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS, errorList, emptyDataSources);
 
  310             } 
catch (NoCurrentCaseException ex) {
 
  311                 String msg = Bundle.AddLogicalImageTask_noCurrentCase();
 
  313                 callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  321                 deleteDestinationDirectory();
 
  323             errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
 
  324             callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
 
  329             progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFiles());
 
  330             addInterestingFiles(interestingFileMap);
 
  331             progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingInterestingFiles());
 
  333                 callback.done(addMultipleImagesTask.getResult(), addMultipleImagesTask.getErrorMessages(), addMultipleImagesTask.getNewDataSources());
 
  335                 callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, errorList, newDataSources);
 
  337         } 
catch (IOException | TskCoreException ex) {
 
  338             errorList.add(Bundle.AddLogicalImageTask_failedToAddInterestingFiles(ex.getMessage()));
 
  339             LOGGER.log(Level.SEVERE, 
"Failed to add interesting files", ex); 
 
  340             callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS, errorList, emptyDataSources);
 
  354         "# {0} - file", 
"# {1} - exception message", 
"AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}" 
  356     private String addReport(Path reportPath, String reportName) {
 
  357         if (!reportPath.toFile().exists()) {
 
  361             Case.getCurrentCase().addReport(reportPath.toString(), 
"LogicalImager", reportName); 
 
  363         } 
catch (TskCoreException ex) {
 
  364             String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage());
 
  365             LOGGER.log(Level.SEVERE, String.format(
"Failed to add report %s. Reason= %s", reportPath.toString(), ex.getMessage()), ex); 
 
  375         LOGGER.log(Level.WARNING, 
"AddLogicalImageTask cancelled, processing may be incomplete"); 
 
  376         synchronized (addMultipleImagesLock) {
 
  378             if (addMultipleImagesTask != null) {
 
  379                 addMultipleImagesTask.cancelTask();
 
  384     private Map<String, Long> imagePathsToDataSourceObjId(Map<Long, List<String>> imagePaths) {
 
  385         Map<String, Long> imagePathToObjId = 
new HashMap<>();
 
  386         for (Map.Entry<Long, List<String>> entry : imagePaths.entrySet()) {
 
  387             Long key = entry.getKey();
 
  388             List<String> names = entry.getValue();
 
  389             for (String name : names) {
 
  390                 imagePathToObjId.put(name, key);
 
  393         return imagePathToObjId;
 
  397         "# {0} - line number", 
"# {1} - fields length", 
"# {2} - expected length", 
"AddLogicalImageTask.notEnoughFields=File does not contain enough fields at line {0}, got {1}, expecting {2}",
 
  398         "# {0} - target image path", 
"AddLogicalImageTask.cannotFindDataSourceObjId=Cannot find obj_id in tsk_image_names for {0}",
 
  399         "# {0} - file number", 
"# {1} - total files", 
"AddLogicalImageTask.addingInterestingFile=Adding interesting files ({0}/{1})",
 
  400         "AddLogicalImageTask.logicalImagerResults=Logical Imager results" 
  402     private void addInterestingFiles(Map<String, List<FileId>> interestingFileMap) throws IOException, TskCoreException {
 
  404         List<BlackboardArtifact> artifacts = 
new ArrayList<>();
 
  406         Iterator<Map.Entry<String, List<FileId>>> iterator = interestingFileMap.entrySet().iterator();
 
  407         while (iterator.hasNext()) {
 
  415             Map.Entry<String, List<FileId>> entry = iterator.next();
 
  416             String key = entry.getKey();
 
  418             String[] split = key.split(
"\t");
 
  421             List<FileId> fileIds = entry.getValue();
 
  422             for (FileId fileId : fileIds) {
 
  424                     postArtifacts(artifacts);
 
  427                 if (lineNumber % REPORT_PROGRESS_INTERVAL == 0) {
 
  428                     progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFile(lineNumber, totalFiles));
 
  430                 if (lineNumber % POST_ARTIFACT_INTERVAL == 0) {
 
  431                     postArtifacts(artifacts);
 
  434                 addInterestingFileToArtifacts(fileId.getFileId(), fileId.getDataSourceId(), Bundle.AddLogicalImageTask_logicalImagerResults(), ruleName, artifacts);
 
  439         postArtifacts(artifacts);
 
  442     private void addInterestingFileToArtifacts(
long fileId, 
long dataSourceId, String ruleSetName, String ruleName, List<BlackboardArtifact> artifacts) 
throws TskCoreException {
 
  443         BlackboardArtifact artifact;
 
  445             artifact = this.blackboard.newAnalysisResult(
 
  446                     BlackboardArtifact.Type.TSK_INTERESTING_FILE_HIT, fileId, dataSourceId,
 
  447                     Score.SCORE_LIKELY_NOTABLE,
 
  448                     null, ruleSetName, null,
 
  450                             new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, ruleSetName),
 
  451                             new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ruleName)
 
  453                     .getAnalysisResult();
 
  454         } 
catch (Blackboard.BlackboardException ex) {
 
  455             throw new TskCoreException(
"Unable to create analysis result.", ex);
 
  458         artifacts.add(artifact);
 
  462         "# {0} - file number", 
"# {1} - total files", 
"AddLogicalImageTask.searchingInterestingFile=Searching for interesting files ({0}/{1})" 
  464     private Map<String, List<FileId>> getInterestingFileMapForVHD(Path resultsPath) 
throws TskCoreException, IOException {
 
  465         Map<Long, List<String>> objIdToimagePathsMap = currentCase.getSleuthkitCase().getImagePaths();
 
  466         imagePathToObjIdMap = imagePathsToDataSourceObjId(objIdToimagePathsMap);
 
  467         Map<String, List<FileId>> interestingFileMap = 
new HashMap<>();
 
  469         try (BufferedReader br = 
new BufferedReader(
new InputStreamReader(
 
  470                 new FileInputStream(resultsPath.toFile()), 
"UTF8"))) { 
 
  474             while ((line = br.readLine()) != null) {
 
  480                 String[] fields = line.split(
"\t", -1); 
 
  481                 if (fields.length != 14) {
 
  482                     throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
 
  484                 String vhdFilename = fields[0];
 
  486                 String fileMetaAddressStr = fields[2];
 
  488                 String ruleSetName = fields[4];
 
  489                 String ruleName = fields[5];
 
  491                 String filename = fields[7];
 
  492                 String parentPath = fields[8];
 
  494                 if (lineNumber % REPORT_PROGRESS_INTERVAL == 0) {
 
  495                     progressMonitor.setProgressText(Bundle.AddLogicalImageTask_searchingInterestingFile(lineNumber, totalFiles));
 
  498                 String query = makeQuery(vhdFilename, fileMetaAddressStr, parentPath, filename);
 
  499                 List<AbstractFile> matchedFiles = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(query);
 
  500                 List<FileId> fileIds = 
new ArrayList<>();
 
  501                 for (AbstractFile file : matchedFiles) {
 
  502                     fileIds.add(
new FileId(file.getDataSourceObjectId(), file.getId()));
 
  504                 String key = String.format(
"%s\t%s", ruleSetName, ruleName);
 
  505                 interestingFileMap.computeIfAbsent(key, (k) -> 
new ArrayList<>())
 
  511         return interestingFileMap;
 
  514     private void postArtifacts(List<BlackboardArtifact> artifacts) {
 
  517             blackboard.postArtifacts(artifacts, MODULE_NAME);
 
  518         } 
catch (Blackboard.BlackboardException ex) {
 
  519             LOGGER.log(Level.SEVERE, 
"Unable to post artifacts to blackboard", ex); 
 
  524         "# {0} - file number", 
"# {1} - total files", 
"AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})" 
  526     private Map<String, List<FileId>> addExtractedFiles(File src, Path resultsPath, Host host, List<Content> newDataSources) 
throws TskCoreException, IOException {
 
  527         SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
 
  528         SleuthkitCase.CaseDbTransaction trans = null;
 
  529         Map<String, List<FileId>> interestingFileMap = 
new HashMap<>();
 
  532             trans = skCase.beginTransaction();
 
  533             LocalFilesDataSource localFilesDataSource = skCase.addLocalFilesDataSource(deviceId, this.src.getName(), timeZone, host, trans);
 
  534             LocalFileImporter fileImporter = 
new LocalFileImporter(skCase, trans);
 
  536             try (BufferedReader br = 
new BufferedReader(
new InputStreamReader(
 
  537                     new FileInputStream(resultsPath.toFile()), 
"UTF8"))) { 
 
  541                 while ((line = br.readLine()) != null) {
 
  543                         rollbackTransaction(trans);
 
  544                         return new HashMap<>();
 
  546                     String[] fields = line.split(
"\t", -1); 
 
  547                     if (fields.length != 14) {
 
  548                         rollbackTransaction(trans);
 
  549                         throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
 
  551                     String vhdFilename = fields[0];
 
  555                     String ruleSetName = fields[4];
 
  556                     String ruleName = fields[5];
 
  558                     String filename = fields[7];
 
  559                     String parentPath = fields[8];
 
  560                     String extractedFilePath = fields[9];
 
  561                     String crtime = fields[10];
 
  562                     String mtime = fields[11];
 
  563                     String atime = fields[12];
 
  564                     String ctime = fields[13];
 
  565                     parentPath = ROOT_STR + 
"/" + vhdFilename + 
"/" + parentPath;
 
  567                     if (lineNumber % REPORT_PROGRESS_INTERVAL == 0) {
 
  568                         progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFile(lineNumber, totalFiles));
 
  572                     AbstractFile fileAdded = fileImporter.addLocalFile(
 
  573                             Paths.get(src.toString(), extractedFilePath).toFile(),
 
  576                             Long.parseLong(ctime),
 
  577                             Long.parseLong(crtime),
 
  578                             Long.parseLong(atime),
 
  579                             Long.parseLong(mtime),
 
  580                             localFilesDataSource);
 
  581                     String key = String.format(
"%s\t%s", ruleSetName, ruleName);
 
  583                     long dataSourceId = fileAdded.getDataSourceObjectId();
 
  584                     long fileId = fileAdded.getId();
 
  585                     interestingFileMap.computeIfAbsent(key, (k) -> 
new ArrayList<>())
 
  586                             .add(
new FileId(dataSourceId, fileId));
 
  591             newDataSources.add(localFilesDataSource);
 
  592             return interestingFileMap;
 
  594         } 
catch (NumberFormatException | TskCoreException ex) {
 
  595             LOGGER.log(Level.SEVERE, 
"Error adding extracted files", ex); 
 
  596             rollbackTransaction(trans);
 
  597             throw new TskCoreException(
"Error adding extracted files", ex);
 
  601     private void rollbackTransaction(SleuthkitCase.CaseDbTransaction trans) throws TskCoreException {
 
  605             } 
catch (TskCoreException ex) {
 
  606                 LOGGER.log(Level.SEVERE, String.format(
"Failed to rollback transaction: %s", ex.getMessage()), ex); 
 
  611     private boolean deleteDestinationDirectory() {
 
  613             FileUtils.deleteDirectory(dest);
 
  614             LOGGER.log(Level.INFO, String.format(
"Cancellation: Deleted directory %s", dest.toString())); 
 
  616         } 
catch (IOException ex) {
 
  617             LOGGER.log(Level.WARNING, String.format(
"Cancellation: Failed to delete directory %s", dest.toString()), ex);  
 
  622     String makeQuery(String vhdFilename, String fileMetaAddressStr, String parentPath, String filename) 
throws TskCoreException {
 
  624         String targetImagePath = Paths.get(dest.toString(), vhdFilename).toString();
 
  625         Long dataSourceObjId = imagePathToObjIdMap.get(targetImagePath);
 
  626         if (dataSourceObjId == null) {
 
  627             throw new TskCoreException(Bundle.AddLogicalImageTask_cannotFindDataSourceObjId(targetImagePath));
 
  629         query = String.format(
"data_source_obj_id = '%s' AND meta_addr = '%s' AND name = '%s'", 
 
  630                 dataSourceObjId.toString(), fileMetaAddressStr, filename.replace(
"'", 
"''"));