From e2779626b2392463772fde93e00e352a47f5257f Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 4 Apr 2019 17:59:51 -0400 Subject: [PATCH 01/10] 4915 Initial commit for fixes/improvements to case node data management --- .../sleuthkit/autopsy/casemodule/Case.java | 49 +-- .../multiusercases/CaseNodeData.java | 290 +++++++++++++----- .../multiusercases/CaseNodeDataCollector.java | 148 ++------- .../autoingest/AutoIngestManager.java | 34 +- .../autoingest/DeleteCaseTask.java | 9 +- 5 files changed, 272 insertions(+), 258 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index b4fc10bf09..c9640f1a13 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -81,6 +81,7 @@ import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent; import org.sleuthkit.autopsy.casemodule.events.DataSourceNameChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ReportAddedEvent; +import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils; import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributeSearchAction; @@ -1620,11 +1621,10 @@ public class Case { } if (getCaseType() == CaseType.MULTI_USER_CASE && !oldCaseDetails.getCaseDisplayName().equals(caseDetails.getCaseDisplayName())) { try { - CoordinationService coordinationService = CoordinationService.getInstance(); - CaseNodeData nodeData = new CaseNodeData(coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory())); + CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory()); nodeData.setDisplayName(caseDetails.getCaseDisplayName()); - coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray()); - } catch (CoordinationServiceException | InterruptedException | IOException ex) { + CaseNodeData.writeCaseNodeData(nodeData); + } catch (CaseNodeDataException | InterruptedException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex); } } @@ -2005,10 +2005,8 @@ public class Case { if (getCaseType() == CaseType.MULTI_USER_CASE) { progressIndicator.progress(Bundle.Case_progressMessage_creatingCaseNodeData()); try { - CoordinationService coordinationService = CoordinationService.getInstance(); - CaseNodeData nodeData = new CaseNodeData(metadata); - coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray()); - } catch (CoordinationServiceException | InterruptedException | ParseException | IOException ex) { + CaseNodeData.createCaseNodeData(metadata); + } catch (CaseNodeDataException | InterruptedException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotCreateCaseNodeData(ex.getLocalizedMessage()), ex); } } @@ -2033,27 +2031,10 @@ public class Case { if (getCaseType() == CaseType.MULTI_USER_CASE) { progressIndicator.progress(Bundle.Case_progressMessage_updatingCaseNodeData()); try { - CaseNodeData nodeData; - CoordinationService coordinationService = CoordinationService.getInstance(); - byte[] nodeBytes = coordinationService.getNodeData(CategoryNode.CASES, metadata.getCaseDirectory()); - if (nodeBytes != null && nodeBytes.length > 0) { - /* - * Update the last access date in the coordination service - * node data for the case. - */ - nodeData = new CaseNodeData(nodeBytes); - nodeData.setLastAccessDate(new Date()); - } else { - /* - * This is a "legacy" case with no data stored in its case - * directory coordination service node yet, or the node is - * empty due to some error, so create the coordination - * service node data from the case metadata. - */ - nodeData = new CaseNodeData(metadata); - } - coordinationService.setNodeData(CategoryNode.CASES, metadata.getCaseDirectory(), nodeData.toArray()); - } catch (CoordinationServiceException | InterruptedException | ParseException | IOException ex) { + CaseNodeData nodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory()); + nodeData.setLastAccessDate(new Date()); + CaseNodeData.writeCaseNodeData(nodeData); + } catch (CaseNodeDataException | InterruptedException ex) { throw new CaseActionException(Bundle.Case_exceptionMessage_couldNotUpdateCaseNodeData(ex.getLocalizedMessage()), ex); } } @@ -2633,9 +2614,8 @@ public class Case { progressIndicator.progress(Bundle.Case_progressMessage_fetchingCoordSvcNodeData()); try { - byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, metadata.getCaseDirectory()); - caseNodeData = new CaseNodeData(nodeBytes); - } catch (CoordinationServiceException | InterruptedException | IOException ex) { + caseNodeData = CaseNodeData.readCaseNodeData(metadata.getCaseDirectory()); + } catch (CaseNodeDataException | InterruptedException ex) { logger.log(Level.SEVERE, String.format("Failed to get coordination service node data %s (%s) in %s", metadata.getCaseDisplayName(), metadata.getCaseName(), metadata.getCaseDirectory()), ex); //NON-NLS throw new CaseActionException(Bundle.Case_exceptionMessage_failedToFetchCoordSvcNodeData(ex.getLocalizedMessage())); } @@ -2899,9 +2879,8 @@ public class Case { private static void setDeletedItemFlag(CaseNodeData caseNodeData, CaseNodeData.DeletedFlags flag) throws InterruptedException { try { caseNodeData.setDeletedFlag(flag); - CoordinationService coordinationService = CoordinationService.getInstance(); - coordinationService.setNodeData(CategoryNode.CASES, caseNodeData.getDirectory().toString(), caseNodeData.toArray()); - } catch (IOException | CoordinationServiceException ex) { + CaseNodeData.writeCaseNodeData(caseNodeData); + } catch (CaseNodeDataException ex) { logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s (%s) in %s", flag.name(), caseNodeData.getDisplayName(), caseNodeData.getName(), caseNodeData.getDirectory()), ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index d825d87ee5..d9e50c563a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -22,25 +22,31 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; +import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.text.ParseException; import java.util.Date; +import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.CaseMetadata; +import org.sleuthkit.autopsy.casemodule.CaseMetadata.CaseMetadataException; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService; +import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; +import org.sleuthkit.autopsy.coreutils.Logger; /** - * An object that converts data for a case directory lock coordination service - * node to and from byte arrays. + * Case data stored in case directory coordination service nodes. */ public final class CaseNodeData { private static final int CURRENT_VERSION = 1; + private static final Logger logger = Logger.getLogger(CaseNodeData.class.getName()); /* * Version 0 fields. */ - private final int version; + private int version; private boolean errorsOccurred; /* @@ -54,26 +60,156 @@ public final class CaseNodeData { private short deletedItemFlags; /** - * Gets the current version of the case directory lock coordination service - * node data. + * Creates case node data from the metadata for a case and writes it to the + * case directory coordination service node, which must already exist. * - * @return The version number. + * @param metadata The case metadata. + * + * @return The case data that was written to the coordination service node. + * + * @throws CaseNodeDataException If there is an error creating or writing + * the case node data. + * @throws InterruptedException If the current thread is interrupted while + * waiting for the coordination service. */ - public static int getCurrentVersion() { - return CaseNodeData.CURRENT_VERSION; + public static CaseNodeData createCaseNodeData(CaseMetadata metadata) throws CaseNodeDataException, InterruptedException { + try { + CaseNodeData nodeData = new CaseNodeData(metadata); + CoordinationService coordinationService = CoordinationService.getInstance(); + coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); + return nodeData; + + } catch (ParseException | IOException | CoordinationServiceException ex) { + throw new CaseNodeDataException(String.format("Failed to create/write case node data for %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS + } } /** - * Uses a CaseMetadata object to construct an object that converts data for - * a case directory lock coordination service node to and from byte arrays. + * Reads case data from a case directory coordination service node. If the + * data is missing, corrupted, or from an older version of the software, an + * attempt is made to remedy the situation using the case metadata. + * + * @param nodePath The case directory coordination service node path. + * + * @return The case node data. + */ + public static CaseNodeData readCaseNodeData(String nodePath) throws CaseNodeDataException, InterruptedException { + try { + CaseNodeData nodeData; + CoordinationService coordinationService = CoordinationService.getInstance(); + byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodePath); + if (nodeBytes != null && nodeBytes.length > 0) { + try { + nodeData = new CaseNodeData(nodeBytes); + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Error reading coordination service node data for %s, will attempt to replace it", nodePath), ex); //NON-NLS + CaseMetadata metadata = getCaseMetadata(nodePath); + nodeData = createCaseNodeData(metadata); + } + } else { + logger.log(Level.INFO, String.format("Missing coordination service node data for %s, will attempt to create it", nodePath)); //NON-NLS + CaseMetadata metadata = getCaseMetadata(nodePath); + nodeData = createCaseNodeData(metadata); + } + if (nodeData.getVersion() < CaseNodeData.CURRENT_VERSION) { + nodeData = upgradeNodeData(nodePath, nodeData); + } + return nodeData; + + } catch (CaseNodeDataException | CaseMetadataException | ParseException | IOException | CoordinationServiceException ex) { + throw new CaseNodeDataException(String.format("Failed to create/write case node data for %s", nodePath.toUpperCase()), ex); //NON-NLS + } + } + + /** + * Writes updated case data to a case directory coordination service node. + * Obtain the case data to be updated by calling createCaseNodeData() or + * readCaseNodeData(). + * + * @param nodeData The case node data. + * + * @throws CaseNodeDataException If there is an error writing the case node + * data. + * @throws InterruptedException If the current thread is interrupted while + * waiting for the coordination service. + */ + public static void writeCaseNodeData(CaseNodeData nodeData) throws CaseNodeDataException, InterruptedException { + try { + CoordinationService coordinationService = CoordinationService.getInstance(); + coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); + } catch (IOException | CoordinationServiceException ex) { + throw new CaseNodeDataException(String.format("Failed to write case node data to %s", nodeData.getDirectory().toString().toUpperCase()), ex); //NON-NLS + } + } + + /** + * Upgrades older versions of node data to the current version and writes + * the data back to the case directory coordination service node. + * + * @param nodePath The case directory coordination service node path. + * @param oldNodeData The outdated node data. + * + * @return The updated node data. + * + * @throws CaseNodeDataException If the case neta data file or case + * directory do not exist. + * @throws CaseMetadataException If the case metadata cannot be read. + */ + private static CaseNodeData upgradeNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { + CaseMetadata metadata = getCaseMetadata(nodePath); + CaseNodeData nodeData = oldNodeData; + if (oldNodeData.getVersion() == 0) { + /* + * Version 0 node data consisted of only the version number and the + * error occurred flag and was only written when an auto ingest job + * error occurred. The version 1 fields need to set from the case + * metadata and the errors occurred flag needs to be carried + * forward. Note that the last accessed date gets advanced to now, + * since it is otherwise unknown. + */ + nodeData = new CaseNodeData(metadata); + nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); + } + writeCaseNodeData(nodeData); + return nodeData; + } + + /** + * Gets the metadata for a case. + * + * @param nodePath The case directory coordination service node path for the + * case. + * + * @return The case metadata. + * + * @throws CaseNodeDataException If the case metadata file or the case + * directory does not exist. + * @throws CaseMetadataException If the case metadata cannot be read. + */ + private static CaseMetadata getCaseMetadata(String nodePath) throws CaseNodeDataException, CaseMetadataException { + Path caseDirectoryPath = Paths.get(nodePath); + File caseDirectory = caseDirectoryPath.toFile(); + if (!caseDirectory.exists()) { + throw new CaseNodeDataException("Case directory does not exist"); // NON-NLS + } + Path metadataFilePath = CaseMetadata.getCaseMetadataFilePath(caseDirectoryPath); + if (metadataFilePath == null) { + throw new CaseNodeDataException("Case meta data file does not exist"); // NON-NLS + } + CaseMetadata metadata = new CaseMetadata(metadataFilePath); + return metadata; + } + + /** + * Constucts an object to use for reading and writing case data stored in + * case directory coordination service nodes from case meta data. * * @param metadata The case meta data. * - * @throws java.text.ParseException If there is an error parsing dates from - * string representations of dates in the - * meta data. + * @throws ParseException If there is an error parsing dates from string + * representations of dates in the meta data. */ - public CaseNodeData(CaseMetadata metadata) throws ParseException { + private CaseNodeData(CaseMetadata metadata) throws ParseException { this.version = CURRENT_VERSION; this.errorsOccurred = false; this.directory = Paths.get(metadata.getCaseDirectory()); @@ -85,33 +221,34 @@ public final class CaseNodeData { } /** - * Uses coordination service node data to construct an object that converts - * data for a case directory lock coordination service node to and from byte - * arrays. + * Constucts an object to use for reading and writing case data stored in + * case directory coordination service nodes from a byte array read from a + * case directory coordination service node. * * @param nodeData The raw bytes received from the coordination service. * * @throws IOException If there is an error reading the node data. */ - public CaseNodeData(byte[] nodeData) throws IOException { + private CaseNodeData(byte[] nodeData) throws IOException { if (nodeData == null || nodeData.length == 0) { throw new IOException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array"); } - DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(nodeData)); - this.version = inputStream.readInt(); - if (this.version > 0) { - this.errorsOccurred = inputStream.readBoolean(); - } else { - short legacyErrorsOccurred = inputStream.readByte(); - this.errorsOccurred = (legacyErrorsOccurred < 0); - } - if (this.version > 0) { - this.directory = Paths.get(inputStream.readUTF()); - this.createDate = new Date(inputStream.readLong()); - this.lastAccessDate = new Date(inputStream.readLong()); - this.name = inputStream.readUTF(); - this.displayName = inputStream.readUTF(); - this.deletedItemFlags = inputStream.readShort(); + try (ByteArrayInputStream byteStream = new ByteArrayInputStream(nodeData); DataInputStream inputStream = new DataInputStream(byteStream)) { + this.version = inputStream.readInt(); + if (this.version > 0) { + this.errorsOccurred = inputStream.readBoolean(); + } else { + byte errorsOccurredByte = inputStream.readByte(); + this.errorsOccurred = (errorsOccurredByte < 0); + } + if (this.version > 0) { + this.directory = Paths.get(inputStream.readUTF()); + this.createDate = new Date(inputStream.readLong()); + this.lastAccessDate = new Date(inputStream.readLong()); + this.name = inputStream.readUTF(); + this.displayName = inputStream.readUTF(); + this.deletedItemFlags = inputStream.readShort(); + } } } @@ -154,16 +291,6 @@ public final class CaseNodeData { return this.directory; } - /** - * Sets the path of the case directory of the case represented by this node - * data. - * - * @param caseDirectory The case directory path. - */ - public void setDirectory(Path caseDirectory) { - this.directory = caseDirectory; - } - /** * Gets the date the case represented by this node data was created. * @@ -173,15 +300,6 @@ public final class CaseNodeData { return new Date(this.createDate.getTime()); } - /** - * Sets the date the case represented by this node data was created. - * - * @param createDate The create date. - */ - public void setCreateDate(Date createDate) { - this.createDate = new Date(createDate.getTime()); - } - /** * Gets the date the case represented by this node data last accessed. * @@ -210,16 +328,6 @@ public final class CaseNodeData { return this.name; } - /** - * Sets the unique and immutable (user cannot change it) name of the case - * represented by this node data. - * - * @param name The case name. - */ - public void setName(String name) { - this.name = name; - } - /** * Gets the display name of the case represented by this node data. * @@ -267,20 +375,20 @@ public final class CaseNodeData { * * @throws IOException If there is an error writing the node data. */ - public byte[] toArray() throws IOException { - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - DataOutputStream outputStream = new DataOutputStream(byteStream); - outputStream.writeInt(this.version); - outputStream.writeBoolean(this.errorsOccurred); - outputStream.writeUTF(this.directory.toString()); - outputStream.writeLong(this.createDate.getTime()); - outputStream.writeLong(this.lastAccessDate.getTime()); - outputStream.writeUTF(this.name); - outputStream.writeUTF(this.displayName); - outputStream.writeShort(this.deletedItemFlags); - outputStream.flush(); - byteStream.flush(); - return byteStream.toByteArray(); + private byte[] toArray() throws IOException { + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { + outputStream.writeInt(this.version); + outputStream.writeBoolean(this.errorsOccurred); + outputStream.writeUTF(this.directory.toString()); + outputStream.writeLong(this.createDate.getTime()); + outputStream.writeLong(this.lastAccessDate.getTime()); + outputStream.writeUTF(this.name); + outputStream.writeUTF(this.displayName); + outputStream.writeShort(this.deletedItemFlags); + outputStream.flush(); + byteStream.flush(); + return byteStream.toByteArray(); + } } /** @@ -316,4 +424,34 @@ public final class CaseNodeData { } + /** + * Exception thrown when there is an error reading or writing case node + * data. + */ + public static final class CaseNodeDataException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Constructs an exception to throw when there is an error reading or + * writing case node data. + * + * @param message The exception message. + */ + private CaseNodeDataException(String message) { + super(message); + } + + /** + * Constructs an exception to throw when there is an error reading or + * writing case node data. + * + * @param message The exception message. + * @param cause The cause of the exception. + */ + private CaseNodeDataException(String message, Throwable cause) { + super(message, cause); + } + } + } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index 5c148aa58d..00f184a5cc 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -18,31 +18,27 @@ */ package org.sleuthkit.autopsy.casemodule.multiusercases; -import java.io.File; -import java.io.IOException; -import java.nio.file.LinkOption; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.text.ParseException; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; -import org.sleuthkit.autopsy.casemodule.CaseMetadata; +import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coreutils.Logger; /** - * Queries the coordination service to collect the multi-user case node data - * stored in the case directory lock ZooKeeper nodes. + * Collects the multi-user case node data stored in the case directory + * coordination service nodes. */ final public class CaseNodeDataCollector { private static final Logger logger = Logger.getLogger(CaseNodeDataCollector.class.getName()); /** - * Queries the coordination service to collect the multi-user case node data - * stored in the case directory lock ZooKeeper nodes. + * Collects the multi-user case node data stored in the case directory + * coordination service nodes. * * @return The node data for the multi-user cases known to the coordination * service. @@ -54,128 +50,32 @@ final public class CaseNodeDataCollector { * service. */ public static List getNodeData() throws CoordinationServiceException, InterruptedException { - final List cases = new ArrayList<>(); - final CoordinationService coordinationService = CoordinationService.getInstance(); - final List nodeList = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES); - for (String nodeName : nodeList) { - if (CoordinationServiceUtils.isCaseNameNodePath(nodeName) - || CoordinationServiceUtils.isCaseResourcesNodePath(nodeName) - || CoordinationServiceUtils.isCaseAutoIngestLogNodePath(nodeName)) { + List nodeDataList = new ArrayList<>(); + CoordinationService coordinationService = CoordinationService.getInstance(); + List nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES); + for (String nodePath : nodePaths) { + /* + * Skip the case name, case resources, and case auto ingest log + * coordination service nodes. They are not used to store case data. + */ + if (CoordinationServiceUtils.isCaseNameNodePath(nodePath) + || CoordinationServiceUtils.isCaseResourcesNodePath(nodePath) + || CoordinationServiceUtils.isCaseAutoIngestLogNodePath(nodePath)) { continue; } /* - * Get the data from the case directory lock node. This data may not - * exist or may exist only in an older version. If it is missing or - * incomplete, create or update it. + * Get the data from the case directory coordination service node. */ try { - CaseNodeData nodeData; - final byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodeName); - if (nodeBytes != null && nodeBytes.length > 0) { - nodeData = new CaseNodeData(nodeBytes); - if (nodeData.getVersion() < CaseNodeData.getCurrentVersion()) { - nodeData = updateNodeData(nodeName, nodeData); - } - } else { - nodeData = updateNodeData(nodeName, null); - } - if (nodeData != null) { - cases.add(nodeData); - } - - } catch (CoordinationService.CoordinationServiceException | InterruptedException | IOException | ParseException | CaseMetadata.CaseMetadataException ex) { - logger.log(Level.SEVERE, String.format("Error getting coordination service node data for %s", nodeName), ex); + CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); + nodeDataList.add(nodeData); + } catch (CaseNodeDataException | InterruptedException ex) { + logger.log(Level.WARNING, String.format("Error reading case node data from %s", nodePath), ex); } } - return cases; - } - - /** - * Updates the case directory lock coordination service node data for a - * case. - * - * @param nodeName The coordination service node name, i.e., the case - * directory path. - * @param oldNodeData The node data to be updated. - * - * @return A CaseNodedata object or null if the coordination service node is - * an "orphan" with no corresponding case directry. - * - * @throws IOException If there is an error writing the - * node data to a byte array. - * @throws CaseMetadataException If there is an error reading the - * case metadata file. - * @throws ParseException If there is an error parsing a date - * from the case metadata file. - * @throws CoordinationServiceException If there is an error interacting - * with the coordination service. - * @throws InterruptedException If a coordination service operation - * is interrupted. - */ - private static CaseNodeData updateNodeData(String nodeName, CaseNodeData oldNodeData) throws IOException, CaseMetadata.CaseMetadataException, ParseException, CoordinationService.CoordinationServiceException, InterruptedException { - Path caseDirectoryPath = Paths.get(nodeName).toRealPath(LinkOption.NOFOLLOW_LINKS); - File caseDirectory = caseDirectoryPath.toFile(); - if (!caseDirectory.exists()) { - logger.log(Level.WARNING, String.format("Found orphan coordination service node %s, attempting clean up", caseDirectoryPath)); - deleteLockNodes(CoordinationService.getInstance(), caseDirectoryPath); - return null; - } - - CaseNodeData nodeData = null; - if (oldNodeData == null || oldNodeData.getVersion() == 0) { - File[] files = caseDirectory.listFiles(); - for (File file : files) { - String name = file.getName().toLowerCase(); - if (name.endsWith(CaseMetadata.getFileExtension())) { - CaseMetadata metadata = new CaseMetadata(Paths.get(file.getAbsolutePath())); - nodeData = new CaseNodeData(metadata); - if (oldNodeData != null) { - /* - * Version 0 case node data was only written if errors - * occurred during an auto ingest job. - */ - nodeData.setErrorsOccurred(true); - } - break; - } - } - } - - if (nodeData != null) { - CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeName, nodeData.toArray()); - } - - return nodeData; - } - - /** - * Attempts to delete the coordination service lock nodes for a case, - * logging any failures. - * - * @param coordinationService The coordination service. - * @param caseDirectoryPath The case directory path. - */ - private static void deleteLockNodes(CoordinationService coordinationService, Path caseDirectoryPath) { - deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseResourcesNodePath(caseDirectoryPath)); - deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseAutoIngestLogNodePath(caseDirectoryPath)); - deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseDirectoryNodePath(caseDirectoryPath)); - deleteCoordinationServiceNode(coordinationService, CoordinationServiceUtils.getCaseNameNodePath(caseDirectoryPath)); - } - - /** - * Attempts to delete a coordination service node, logging failure. - * - * @param coordinationService The coordination service. - * @param nodeName A node name. - */ - private static void deleteCoordinationServiceNode(CoordinationService coordinationService, String nodeName) { - try { - coordinationService.deleteNode(CoordinationService.CategoryNode.CASES, nodeName); - } catch (CoordinationService.CoordinationServiceException | InterruptedException ex) { - logger.log(Level.WARNING, String.format("Error deleting coordination service node %s", nodeName), ex); - } + return nodeDataList; } /** diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index a85a4993ae..e492aba3b3 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -62,6 +62,7 @@ import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseDetails; import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; +import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Lock; @@ -1016,22 +1017,19 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen * * @param caseDirectoryPath The case directory path. * - * @throws CoordinationServiceException If there was an error getting the - * node data from the cooordination - * service. - * @throws IOException If the node data was missing or - * there was an error interpreting it. - * @throws InterruptedException If the thread running the input - * directory scan task is interrupted - * while blocked, i.e., if auto ingest - * is shutting down. + * @throws InterruptedException If the thread running the input directory + * scan task is interrupted while blocked, + * i.e., if auto ingest is shutting down. */ - private void setCaseNodeDataErrorsOccurred(Path caseDirectoryPath) throws IOException, CoordinationServiceException, InterruptedException { - byte[] rawData = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, caseDirectoryPath.toString()); - CaseNodeData caseNodeData = new CaseNodeData(rawData); - caseNodeData.setErrorsOccurred(true); - rawData = caseNodeData.toArray(); - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, caseDirectoryPath.toString(), rawData); + private void setCaseNodeDataErrorsOccurred(Path caseDirectoryPath) throws InterruptedException { + try { + CaseNodeData caseNodeData = CaseNodeData.readCaseNodeData(caseDirectoryPath.toString()); + caseNodeData.setErrorsOccurred(true); + CaseNodeData.writeCaseNodeData(caseNodeData); + } catch (CaseNodeDataException ex) { + sysLogger.log(Level.WARNING, String.format("Error attempting to set error flag in case node data for %s", caseDirectoryPath), ex); + } + } /** @@ -1381,11 +1379,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen if (null != caseDirectoryPath) { job.setCaseDirectoryPath(caseDirectoryPath); job.setErrorsOccurred(true); - try { - setCaseNodeDataErrorsOccurred(caseDirectoryPath); - } catch (IOException ex) { - sysLogger.log(Level.WARNING, String.format("Error attempting to set error flag in case node data for %s", caseDirectoryPath), ex); - } + setCaseNodeDataErrorsOccurred(caseDirectoryPath); } else { job.setErrorsOccurred(false); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteCaseTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteCaseTask.java index 31447ce2a1..54ac4f504c 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteCaseTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/DeleteCaseTask.java @@ -37,6 +37,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CaseMetadata; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData; +import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; import org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CategoryNode; @@ -785,12 +786,14 @@ final class DeleteCaseTask implements Runnable { * case. * * @param flag The flag to set. + * + * @throws InterruptedException If the interrupted flag is set. */ - private void setDeletedItemFlag(CaseNodeData.DeletedFlags flag) { + private void setDeletedItemFlag(CaseNodeData.DeletedFlags flag) throws InterruptedException { try { caseNodeData.setDeletedFlag(flag); - coordinationService.setNodeData(CategoryNode.CASES, caseNodeData.getDirectory().toString(), caseNodeData.toArray()); - } catch (IOException | CoordinationServiceException | InterruptedException ex) { + CaseNodeData.writeCaseNodeData(caseNodeData); + } catch (CaseNodeDataException ex) { logger.log(Level.SEVERE, String.format("Error updating deleted item flag %s for %s", flag.name(), caseNodeData.getDisplayName()), ex); } } From 46512935330ffc2d69c873871bd9722736bd1610 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 4 Apr 2019 18:12:36 -0400 Subject: [PATCH 02/10] 4915 First cleanup commit for fixes/improvements to case node data management --- .../casemodule/multiusercases/CaseNodeData.java | 13 +++++++++---- .../multiusercases/CaseNodeDataCollector.java | 2 -- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index d9e50c563a..35e9f4d76f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -92,6 +92,11 @@ public final class CaseNodeData { * @param nodePath The case directory coordination service node path. * * @return The case node data. + * + * @throws CaseNodeDataException If there is an error writing the case node + * data. + * @throws InterruptedException If the current thread is interrupted while + * waiting for the coordination service. */ public static CaseNodeData readCaseNodeData(String nodePath) throws CaseNodeDataException, InterruptedException { try { @@ -102,17 +107,17 @@ public final class CaseNodeData { try { nodeData = new CaseNodeData(nodeBytes); } catch (IOException ex) { - logger.log(Level.WARNING, String.format("Error reading coordination service node data for %s, will attempt to replace it", nodePath), ex); //NON-NLS + logger.log(Level.WARNING, String.format("Error reading coordination service node data for %s, will attempt to replace it", nodePath.toUpperCase()), ex); //NON-NLS CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); } } else { - logger.log(Level.INFO, String.format("Missing coordination service node data for %s, will attempt to create it", nodePath)); //NON-NLS + logger.log(Level.INFO, String.format("Missing coordination service node data for %s, will attempt to create it", nodePath.toUpperCase())); //NON-NLS CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); } if (nodeData.getVersion() < CaseNodeData.CURRENT_VERSION) { - nodeData = upgradeNodeData(nodePath, nodeData); + nodeData = upgradeCaseNodeData(nodePath, nodeData); } return nodeData; @@ -155,7 +160,7 @@ public final class CaseNodeData { * directory do not exist. * @throws CaseMetadataException If the case metadata cannot be read. */ - private static CaseNodeData upgradeNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { + private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { CaseMetadata metadata = getCaseMetadata(nodePath); CaseNodeData nodeData = oldNodeData; if (oldNodeData.getVersion() == 0) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index 00f184a5cc..a1241f8f3c 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.casemodule.multiusercases; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; From fdbef6373ddbfd58ee1c8b32ed560ec980d73be7 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 5 Apr 2019 13:56:30 -0400 Subject: [PATCH 03/10] CaseNodeData version 2.0 (includes minor ver) --- .../multiusercases/CaseNodeData.java | 50 +++++++++++++++---- .../multiusercases/CaseNodeDataCollector.java | 9 ++-- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index 35e9f4d76f..9e80373b4b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -40,11 +40,14 @@ import org.sleuthkit.autopsy.coreutils.Logger; */ public final class CaseNodeData { - private static final int CURRENT_VERSION = 1; + private static final int MAJOR_VERSION = 2; + private static final int MINOR_VERSION = 0; private static final Logger logger = Logger.getLogger(CaseNodeData.class.getName()); /* - * Version 0 fields. + * Version 0 fields. Note that version 0 node data was only written to the + * coordination service node if an auto ingest job error occurred and the + * errorsOccurred field needed to be set. */ private int version; private boolean errorsOccurred; @@ -59,6 +62,11 @@ public final class CaseNodeData { private String displayName; private short deletedItemFlags; + /* + * Version 2 fields. + */ + private int minorVersion; + /** * Creates case node data from the metadata for a case and writes it to the * case directory coordination service node, which must already exist. @@ -116,7 +124,7 @@ public final class CaseNodeData { CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); } - if (nodeData.getVersion() < CaseNodeData.CURRENT_VERSION) { + if (nodeData.getVersion() < CaseNodeData.MAJOR_VERSION) { nodeData = upgradeCaseNodeData(nodePath, nodeData); } return nodeData; @@ -162,18 +170,26 @@ public final class CaseNodeData { */ private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { CaseMetadata metadata = getCaseMetadata(nodePath); - CaseNodeData nodeData = oldNodeData; + CaseNodeData nodeData; if (oldNodeData.getVersion() == 0) { /* * Version 0 node data consisted of only the version number and the * error occurred flag and was only written when an auto ingest job - * error occurred. The version 1 fields need to set from the case + * error occurred. The version 1 fields need to be set from the case * metadata and the errors occurred flag needs to be carried * forward. Note that the last accessed date gets advanced to now, * since it is otherwise unknown. */ nodeData = new CaseNodeData(metadata); nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); + } else if (oldNodeData.getVersion() == 1) { + /* + * Version 1 node data did not have a minor version number field. + */ + oldNodeData.setMinorVersion(MINOR_VERSION); + nodeData = oldNodeData; + } else { + nodeData = oldNodeData; } writeCaseNodeData(nodeData); return nodeData; @@ -215,7 +231,7 @@ public final class CaseNodeData { * representations of dates in the meta data. */ private CaseNodeData(CaseMetadata metadata) throws ParseException { - this.version = CURRENT_VERSION; + this.version = MAJOR_VERSION; this.errorsOccurred = false; this.directory = Paths.get(metadata.getCaseDirectory()); this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate()); @@ -223,6 +239,7 @@ public final class CaseNodeData { this.name = metadata.getCaseName(); this.displayName = metadata.getCaseDisplayName(); this.deletedItemFlags = 0; + this.minorVersion = MINOR_VERSION; } /** @@ -240,7 +257,7 @@ public final class CaseNodeData { } try (ByteArrayInputStream byteStream = new ByteArrayInputStream(nodeData); DataInputStream inputStream = new DataInputStream(byteStream)) { this.version = inputStream.readInt(); - if (this.version > 0) { + if (this.version == 1) { this.errorsOccurred = inputStream.readBoolean(); } else { byte errorsOccurredByte = inputStream.readByte(); @@ -254,18 +271,30 @@ public final class CaseNodeData { this.displayName = inputStream.readUTF(); this.deletedItemFlags = inputStream.readShort(); } + if (this.version > 1) { + this.minorVersion = inputStream.readInt(); + } } } /** - * Gets the node data version number of this node. + * Gets the version number of this node data. * * @return The version number. */ - public int getVersion() { + private int getVersion() { return this.version; } + /** + * Sets the version number of this node data. + * + * @param version The version number. + */ + private void setMinorVersion(int minorVersion) { + this.minorVersion = minorVersion; + } + /** * Gets whether or not any errors occurred during the processing of any auto * ingest job for the case represented by this node data. @@ -383,13 +412,14 @@ public final class CaseNodeData { private byte[] toArray() throws IOException { try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { outputStream.writeInt(this.version); - outputStream.writeBoolean(this.errorsOccurred); + outputStream.writeByte((byte) (this.errorsOccurred ? 0x80 : 0)); outputStream.writeUTF(this.directory.toString()); outputStream.writeLong(this.createDate.getTime()); outputStream.writeLong(this.lastAccessDate.getTime()); outputStream.writeUTF(this.name); outputStream.writeUTF(this.displayName); outputStream.writeShort(this.deletedItemFlags); + outputStream.writeInt(this.minorVersion); outputStream.flush(); byteStream.flush(); return byteStream.toByteArray(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index a1241f8f3c..05cb84da4f 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -22,6 +22,9 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.multiusercases.CaseNodeData.CaseNodeDataException; +import static org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils.isCaseAutoIngestLogNodePath; +import static org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils.isCaseNameNodePath; +import static org.sleuthkit.autopsy.casemodule.multiusercases.CoordinationServiceUtils.isCaseResourcesNodePath; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; import org.sleuthkit.autopsy.coreutils.Logger; @@ -56,14 +59,12 @@ final public class CaseNodeDataCollector { * Skip the case name, case resources, and case auto ingest log * coordination service nodes. They are not used to store case data. */ - if (CoordinationServiceUtils.isCaseNameNodePath(nodePath) - || CoordinationServiceUtils.isCaseResourcesNodePath(nodePath) - || CoordinationServiceUtils.isCaseAutoIngestLogNodePath(nodePath)) { + if (isCaseNameNodePath(nodePath) || isCaseResourcesNodePath(nodePath) || isCaseAutoIngestLogNodePath(nodePath)) { continue; } /* - * Get the data from the case directory coordination service node. + * Get the case node data from the case directory coordination service node. */ try { CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); From 6130def6fda2188dcbf619612a7a1083385e233c Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 17:30:18 -0400 Subject: [PATCH 04/10] Codacy issue resolution and misc polish for case node data fixes --- .../multiusercases/CaseNodeData.java | 106 +++++++++--------- .../multiusercases/CaseNodeDataCollector.java | 48 ++++++++ 2 files changed, 104 insertions(+), 50 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index 9e80373b4b..99d7b19274 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -46,8 +46,7 @@ public final class CaseNodeData { /* * Version 0 fields. Note that version 0 node data was only written to the - * coordination service node if an auto ingest job error occurred and the - * errorsOccurred field needed to be set. + * coordination service node if an auto ingest job error occurred. */ private int version; private boolean errorsOccurred; @@ -69,26 +68,27 @@ public final class CaseNodeData { /** * Creates case node data from the metadata for a case and writes it to the - * case directory coordination service node, which must already exist. + * appropriate case directory coordination service node, which must already + * exist. * * @param metadata The case metadata. * - * @return The case data that was written to the coordination service node. + * @return The case node data that was written to the coordination service + * node. * * @throws CaseNodeDataException If there is an error creating or writing * the case node data. * @throws InterruptedException If the current thread is interrupted while * waiting for the coordination service. */ - public static CaseNodeData createCaseNodeData(CaseMetadata metadata) throws CaseNodeDataException, InterruptedException { + public static CaseNodeData createCaseNodeData(final CaseMetadata metadata) throws CaseNodeDataException, InterruptedException { try { - CaseNodeData nodeData = new CaseNodeData(metadata); - CoordinationService coordinationService = CoordinationService.getInstance(); - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); + final CaseNodeData nodeData = new CaseNodeData(metadata); + CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); return nodeData; } catch (ParseException | IOException | CoordinationServiceException ex) { - throw new CaseNodeDataException(String.format("Failed to create/write case node data for %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS + throw new CaseNodeDataException(String.format("Failed to create case node data for %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS } } @@ -101,27 +101,34 @@ public final class CaseNodeData { * * @return The case node data. * - * @throws CaseNodeDataException If there is an error writing the case node - * data. + * @throws CaseNodeDataException If there is an error reading or writing the + * case node data. * @throws InterruptedException If the current thread is interrupted while * waiting for the coordination service. */ public static CaseNodeData readCaseNodeData(String nodePath) throws CaseNodeDataException, InterruptedException { try { CaseNodeData nodeData; - CoordinationService coordinationService = CoordinationService.getInstance(); - byte[] nodeBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodePath); + final byte[] nodeBytes = CoordinationService.getInstance().getNodeData(CoordinationService.CategoryNode.CASES, nodePath); if (nodeBytes != null && nodeBytes.length > 0) { try { nodeData = new CaseNodeData(nodeBytes); } catch (IOException ex) { + /* + * The existing case node data is corrupted. + */ logger.log(Level.WARNING, String.format("Error reading coordination service node data for %s, will attempt to replace it", nodePath.toUpperCase()), ex); //NON-NLS - CaseMetadata metadata = getCaseMetadata(nodePath); + final CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); } } else { + /* + * The case node data is missing. Version 0 node data was only + * written to the coordination service node if an auto ingest + * job error occurred. + */ logger.log(Level.INFO, String.format("Missing coordination service node data for %s, will attempt to create it", nodePath.toUpperCase())); //NON-NLS - CaseMetadata metadata = getCaseMetadata(nodePath); + final CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); } if (nodeData.getVersion() < CaseNodeData.MAJOR_VERSION) { @@ -130,14 +137,14 @@ public final class CaseNodeData { return nodeData; } catch (CaseNodeDataException | CaseMetadataException | ParseException | IOException | CoordinationServiceException ex) { - throw new CaseNodeDataException(String.format("Failed to create/write case node data for %s", nodePath.toUpperCase()), ex); //NON-NLS + throw new CaseNodeDataException(String.format("Failed to read or write case node data for %s", nodePath.toUpperCase()), ex); //NON-NLS } } /** - * Writes updated case data to a case directory coordination service node. - * Obtain the case data to be updated by calling createCaseNodeData() or - * readCaseNodeData(). + * Writes case data to a case directory coordination service node. Obtain + * the case data to be updated and written by calling createCaseNodeData() + * or readCaseNodeData(). * * @param nodeData The case node data. * @@ -148,8 +155,8 @@ public final class CaseNodeData { */ public static void writeCaseNodeData(CaseNodeData nodeData) throws CaseNodeDataException, InterruptedException { try { - CoordinationService coordinationService = CoordinationService.getInstance(); - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); + CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); + } catch (IOException | CoordinationServiceException ex) { throw new CaseNodeDataException(String.format("Failed to write case node data to %s", nodeData.getDirectory().toString().toUpperCase()), ex); //NON-NLS } @@ -164,32 +171,35 @@ public final class CaseNodeData { * * @return The updated node data. * - * @throws CaseNodeDataException If the case neta data file or case + * @throws CaseNodeDataException If the case meta data file or case * directory do not exist. * @throws CaseMetadataException If the case metadata cannot be read. */ private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { - CaseMetadata metadata = getCaseMetadata(nodePath); + final CaseMetadata metadata = getCaseMetadata(nodePath); CaseNodeData nodeData; if (oldNodeData.getVersion() == 0) { /* * Version 0 node data consisted of only the version number and the - * error occurred flag and was only written when an auto ingest job - * error occurred. The version 1 fields need to be set from the case - * metadata and the errors occurred flag needs to be carried - * forward. Note that the last accessed date gets advanced to now, - * since it is otherwise unknown. + * errors occurred flag and was only written when an auto ingest job + * error occurred. To upgrade from version 0, the version 1 fields + * need to be set from the case metadata and the errors occurred + * flag needs to be carried forward. Note that the last accessed + * date gets advanced to now, since it is otherwise unknown. */ nodeData = new CaseNodeData(metadata); nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); + } else if (oldNodeData.getVersion() == 1) { /* * Version 1 node data did not have a minor version number field. */ oldNodeData.setMinorVersion(MINOR_VERSION); nodeData = oldNodeData; + } else { nodeData = oldNodeData; + } writeCaseNodeData(nodeData); return nodeData; @@ -208,17 +218,16 @@ public final class CaseNodeData { * @throws CaseMetadataException If the case metadata cannot be read. */ private static CaseMetadata getCaseMetadata(String nodePath) throws CaseNodeDataException, CaseMetadataException { - Path caseDirectoryPath = Paths.get(nodePath); - File caseDirectory = caseDirectoryPath.toFile(); + final Path caseDirectoryPath = Paths.get(nodePath); + final File caseDirectory = caseDirectoryPath.toFile(); if (!caseDirectory.exists()) { throw new CaseNodeDataException("Case directory does not exist"); // NON-NLS } - Path metadataFilePath = CaseMetadata.getCaseMetadataFilePath(caseDirectoryPath); + final Path metadataFilePath = CaseMetadata.getCaseMetadataFilePath(caseDirectoryPath); if (metadataFilePath == null) { throw new CaseNodeDataException("Case meta data file does not exist"); // NON-NLS } - CaseMetadata metadata = new CaseMetadata(metadataFilePath); - return metadata; + return new CaseMetadata(metadataFilePath); } /** @@ -287,7 +296,7 @@ public final class CaseNodeData { } /** - * Sets the version number of this node data. + * Sets the minor version number of this node data. * * @param version The version number. */ @@ -297,7 +306,7 @@ public final class CaseNodeData { /** * Gets whether or not any errors occurred during the processing of any auto - * ingest job for the case represented by this node data. + * ingest job for the case. * * @return True or false. */ @@ -307,7 +316,7 @@ public final class CaseNodeData { /** * Sets whether or not any errors occurred during the processing of any auto - * ingest job for the case represented by this node data. + * ingest job for the case. * * @param errorsOccurred True or false. */ @@ -316,8 +325,7 @@ public final class CaseNodeData { } /** - * Gets the path of the case directory of the case represented by this node - * data. + * Gets the path of the case directory. * * @return The case directory path. */ @@ -326,7 +334,7 @@ public final class CaseNodeData { } /** - * Gets the date the case represented by this node data was created. + * Gets the date the case was created. * * @return The create date. */ @@ -335,7 +343,7 @@ public final class CaseNodeData { } /** - * Gets the date the case represented by this node data last accessed. + * Gets the date the case was last accessed. * * @return The last access date. */ @@ -344,7 +352,7 @@ public final class CaseNodeData { } /** - * Sets the date the case represented by this node data was last accessed. + * Sets the date the case was last accessed. * * @param lastAccessDate The last access date. */ @@ -353,8 +361,7 @@ public final class CaseNodeData { } /** - * Gets the unique and immutable (user cannot change it) name of the case - * represented by this node data. + * Gets the unique and immutable name of the case. * * @return The case name. */ @@ -363,7 +370,7 @@ public final class CaseNodeData { } /** - * Gets the display name of the case represented by this node data. + * Gets the display name of the case. * * @return The case display name. */ @@ -372,7 +379,7 @@ public final class CaseNodeData { } /** - * Sets the display name of the case represented by this node data. + * Sets the display name of the case. * * @param displayName The case display name. */ @@ -381,19 +388,18 @@ public final class CaseNodeData { } /** - * Checks whether a deleted item flag is set for the case represented by - * this node data. + * Checks whether a given deleted item flag is set for the case. * * @param flag The flag to check. * - * @return + * @return True or false. */ public boolean isDeletedFlagSet(DeletedFlags flag) { return (this.deletedItemFlags & flag.getValue()) == flag.getValue(); } /** - * Sets a deleted item flag for the case represented by this node data. + * Sets a given deleted item flag. * * @param flag The flag to set. */ @@ -407,7 +413,7 @@ public final class CaseNodeData { * * @return The node data as a byte array. * - * @throws IOException If there is an error writing the node data. + * @throws IOException If there is an error writing the node data to the array. */ private byte[] toArray() throws IOException { try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index 05cb84da4f..bef5c9c056 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -83,4 +83,52 @@ final public class CaseNodeDataCollector { private CaseNodeDataCollector() { } +// private static void runTests(CoordinationService coordinationService) { +// String nodePath = "\\\\fstore\\forensics\\Viking\\Output\\rcordovano\\cordovano_case00_2019_04_01_13_56_50"; +// byte[] originalBytes; +// try { +// originalBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodePath); +// } catch (CoordinationServiceException | InterruptedException ex) { +// return; +// } +// +// try { +// // Version 1 to version 2 upgrade +// CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); +// +// // Version 0 to version 2 upgrade +// try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { +// outputStream.writeInt(0); +// outputStream.writeByte((byte) 0x80); +// byteStream.flush(); +// byteStream.toByteArray(); +// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); +// CaseNodeData.readCaseNodeData(nodePath); +// } +// +// // Corrupt data repair +// try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { +// outputStream.writeInt(2); +// outputStream.writeBoolean(false); +// outputStream.writeInt(3); +// outputStream.writeInt(4); +// byteStream.toByteArray(); +// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); +// CaseNodeData.readCaseNodeData(nodePath); +// } +// +// // Missing data replacement +// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, null); +// CaseNodeData.readCaseNodeData(nodePath); +// +// } catch (Exception ex) { +// logger.log(Level.SEVERE, "Error", ex); +// } finally { +// try { +// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, originalBytes); +// } catch (CoordinationServiceException | InterruptedException ex) { +// } +// } +// } + } From d3ef20fd064c82f12a96a745318b398aacd9a913 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 18:04:37 -0400 Subject: [PATCH 05/10] Misc polish for case node data fixes --- .../multiusercases/CaseNodeData.java | 34 ++++--- .../multiusercases/CaseNodeDataCollector.java | 97 ++++++++++--------- 2 files changed, 68 insertions(+), 63 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index 99d7b19274..10f95a5bce 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.coordinationservice.CoordinationService.Coordinatio import org.sleuthkit.autopsy.coreutils.Logger; /** - * Case data stored in case directory coordination service nodes. + * Case data stored in a case directory coordination service node. */ public final class CaseNodeData { @@ -88,7 +88,7 @@ public final class CaseNodeData { return nodeData; } catch (ParseException | IOException | CoordinationServiceException ex) { - throw new CaseNodeDataException(String.format("Failed to create case node data for %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS + throw new CaseNodeDataException(String.format("Error creating case node data for coordination service node with path %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS } } @@ -117,9 +117,10 @@ public final class CaseNodeData { /* * The existing case node data is corrupted. */ - logger.log(Level.WARNING, String.format("Error reading coordination service node data for %s, will attempt to replace it", nodePath.toUpperCase()), ex); //NON-NLS + logger.log(Level.WARNING, String.format("Error reading node data for coordination service node with path %s, will attempt to replace it", nodePath.toUpperCase()), ex); //NON-NLS final CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); + logger.log(Level.INFO, String.format("Replaced corrupt node data for coordination service node with path %s", nodePath.toUpperCase())); //NON-NLS } } else { /* @@ -127,9 +128,10 @@ public final class CaseNodeData { * written to the coordination service node if an auto ingest * job error occurred. */ - logger.log(Level.INFO, String.format("Missing coordination service node data for %s, will attempt to create it", nodePath.toUpperCase())); //NON-NLS + logger.log(Level.INFO, String.format("Missing node data for coordination service node with path %s, will attempt to create it", nodePath.toUpperCase())); //NON-NLS final CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = createCaseNodeData(metadata); + logger.log(Level.INFO, String.format("Created node data for coordination service node with path %s", nodePath.toUpperCase())); //NON-NLS } if (nodeData.getVersion() < CaseNodeData.MAJOR_VERSION) { nodeData = upgradeCaseNodeData(nodePath, nodeData); @@ -137,7 +139,7 @@ public final class CaseNodeData { return nodeData; } catch (CaseNodeDataException | CaseMetadataException | ParseException | IOException | CoordinationServiceException ex) { - throw new CaseNodeDataException(String.format("Failed to read or write case node data for %s", nodePath.toUpperCase()), ex); //NON-NLS + throw new CaseNodeDataException(String.format("Error reading/writing node data coordination service node with path %s", nodePath.toUpperCase()), ex); //NON-NLS } } @@ -156,9 +158,9 @@ public final class CaseNodeData { public static void writeCaseNodeData(CaseNodeData nodeData) throws CaseNodeDataException, InterruptedException { try { CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray()); - + } catch (IOException | CoordinationServiceException ex) { - throw new CaseNodeDataException(String.format("Failed to write case node data to %s", nodeData.getDirectory().toString().toUpperCase()), ex); //NON-NLS + throw new CaseNodeDataException(String.format("Error writing node data coordination service node with path %s", nodeData.getDirectory().toString().toUpperCase()), ex); //NON-NLS } } @@ -189,17 +191,17 @@ public final class CaseNodeData { */ nodeData = new CaseNodeData(metadata); nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); - + } else if (oldNodeData.getVersion() == 1) { /* * Version 1 node data did not have a minor version number field. */ oldNodeData.setMinorVersion(MINOR_VERSION); nodeData = oldNodeData; - + } else { nodeData = oldNodeData; - + } writeCaseNodeData(nodeData); return nodeData; @@ -231,8 +233,8 @@ public final class CaseNodeData { } /** - * Constucts an object to use for reading and writing case data stored in - * case directory coordination service nodes from case meta data. + * Uses case metadata to construct the case data to store in a case + * directory coordination service node. * * @param metadata The case meta data. * @@ -252,9 +254,8 @@ public final class CaseNodeData { } /** - * Constucts an object to use for reading and writing case data stored in - * case directory coordination service nodes from a byte array read from a - * case directory coordination service node. + * Uses the raw bytes from a case directory coordination service node to + * construct a case node data object. * * @param nodeData The raw bytes received from the coordination service. * @@ -413,7 +414,8 @@ public final class CaseNodeData { * * @return The node data as a byte array. * - * @throws IOException If there is an error writing the node data to the array. + * @throws IOException If there is an error writing the node data to the + * array. */ private byte[] toArray() throws IOException { try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index bef5c9c056..7b6814cd5b 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.casemodule.multiusercases; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -53,6 +55,7 @@ final public class CaseNodeDataCollector { public static List getNodeData() throws CoordinationServiceException, InterruptedException { List nodeDataList = new ArrayList<>(); CoordinationService coordinationService = CoordinationService.getInstance(); + runTests(coordinationService); List nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES); for (String nodePath : nodePaths) { /* @@ -83,52 +86,52 @@ final public class CaseNodeDataCollector { private CaseNodeDataCollector() { } -// private static void runTests(CoordinationService coordinationService) { -// String nodePath = "\\\\fstore\\forensics\\Viking\\Output\\rcordovano\\cordovano_case00_2019_04_01_13_56_50"; -// byte[] originalBytes; -// try { -// originalBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodePath); -// } catch (CoordinationServiceException | InterruptedException ex) { -// return; -// } -// -// try { -// // Version 1 to version 2 upgrade -// CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); -// -// // Version 0 to version 2 upgrade -// try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { -// outputStream.writeInt(0); -// outputStream.writeByte((byte) 0x80); -// byteStream.flush(); -// byteStream.toByteArray(); -// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); -// CaseNodeData.readCaseNodeData(nodePath); -// } -// -// // Corrupt data repair -// try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { -// outputStream.writeInt(2); -// outputStream.writeBoolean(false); -// outputStream.writeInt(3); -// outputStream.writeInt(4); -// byteStream.toByteArray(); -// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); -// CaseNodeData.readCaseNodeData(nodePath); -// } -// -// // Missing data replacement -// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, null); -// CaseNodeData.readCaseNodeData(nodePath); -// -// } catch (Exception ex) { -// logger.log(Level.SEVERE, "Error", ex); -// } finally { -// try { -// coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, originalBytes); -// } catch (CoordinationServiceException | InterruptedException ex) { -// } -// } -// } + private static void runTests(CoordinationService coordinationService) { + String nodePath = "\\\\FSTORE\\FORENSICS\\VIKING\\OUTPUT\\RCORDOVANO\\CORDOVANO_CASE0_2019_04_07_17_26_04"; + byte[] originalBytes; + try { + originalBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodePath); + } catch (CoordinationServiceException | InterruptedException ex) { + return; + } + + try { + // Version 1 to version 2 upgrade + CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); + + // Version 0 to version 2 upgrade + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { + outputStream.writeInt(0); + outputStream.writeByte((byte) 0x80); + byteStream.flush(); + byteStream.toByteArray(); + coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); + CaseNodeData.readCaseNodeData(nodePath); + } + + // Corrupt data repair + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { + outputStream.writeInt(2); + outputStream.writeBoolean(false); + outputStream.writeInt(3); + outputStream.writeInt(4); + byteStream.toByteArray(); + coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); + CaseNodeData.readCaseNodeData(nodePath); + } + + // Missing data replacement + coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, null); + CaseNodeData.readCaseNodeData(nodePath); + + } catch (Exception ex) { + logger.log(Level.SEVERE, "Error", ex); + } finally { + try { + coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, originalBytes); + } catch (CoordinationServiceException | InterruptedException ex) { + } + } + } } From 62a297be18654c52b600ec005291df7e88e7e878 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 18:42:12 -0400 Subject: [PATCH 06/10] Misc polish for case node data fixes --- .../multiusercases/CaseNodeDataCollector.java | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index 7b6814cd5b..8a5bb36f7f 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -55,7 +55,6 @@ final public class CaseNodeDataCollector { public static List getNodeData() throws CoordinationServiceException, InterruptedException { List nodeDataList = new ArrayList<>(); CoordinationService coordinationService = CoordinationService.getInstance(); - runTests(coordinationService); List nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES); for (String nodePath : nodePaths) { /* @@ -85,53 +84,5 @@ final public class CaseNodeDataCollector { */ private CaseNodeDataCollector() { } - - private static void runTests(CoordinationService coordinationService) { - String nodePath = "\\\\FSTORE\\FORENSICS\\VIKING\\OUTPUT\\RCORDOVANO\\CORDOVANO_CASE0_2019_04_07_17_26_04"; - byte[] originalBytes; - try { - originalBytes = coordinationService.getNodeData(CoordinationService.CategoryNode.CASES, nodePath); - } catch (CoordinationServiceException | InterruptedException ex) { - return; - } - - try { - // Version 1 to version 2 upgrade - CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); - - // Version 0 to version 2 upgrade - try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { - outputStream.writeInt(0); - outputStream.writeByte((byte) 0x80); - byteStream.flush(); - byteStream.toByteArray(); - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); - CaseNodeData.readCaseNodeData(nodePath); - } - - // Corrupt data repair - try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) { - outputStream.writeInt(2); - outputStream.writeBoolean(false); - outputStream.writeInt(3); - outputStream.writeInt(4); - byteStream.toByteArray(); - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, byteStream.toByteArray()); - CaseNodeData.readCaseNodeData(nodePath); - } - - // Missing data replacement - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, null); - CaseNodeData.readCaseNodeData(nodePath); - - } catch (Exception ex) { - logger.log(Level.SEVERE, "Error", ex); - } finally { - try { - coordinationService.setNodeData(CoordinationService.CategoryNode.CASES, nodePath, originalBytes); - } catch (CoordinationServiceException | InterruptedException ex) { - } - } - } } From 7097eae4c641db694154b7636b239e25936f3e89 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 18:44:56 -0400 Subject: [PATCH 07/10] Misc polish for case node data fixes --- .../casemodule/multiusercases/CaseNodeDataCollector.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index 8a5bb36f7f..28f7450f5c 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.casemodule.multiusercases; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; From 9c22553bf82546a9b3ee0e0c79d003d955cd5795 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 18:50:42 -0400 Subject: [PATCH 08/10] Misc polish for case node data fixes --- .../casemodule/multiusercases/CaseNodeDataCollector.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java index 28f7450f5c..cfb542d967 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeDataCollector.java @@ -51,9 +51,9 @@ final public class CaseNodeDataCollector { * service. */ public static List getNodeData() throws CoordinationServiceException, InterruptedException { - List nodeDataList = new ArrayList<>(); - CoordinationService coordinationService = CoordinationService.getInstance(); - List nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES); + final List nodeDataList = new ArrayList<>(); + final CoordinationService coordinationService = CoordinationService.getInstance(); + final List nodePaths = coordinationService.getNodeList(CoordinationService.CategoryNode.CASES); for (String nodePath : nodePaths) { /* * Skip the case name, case resources, and case auto ingest log @@ -67,7 +67,7 @@ final public class CaseNodeDataCollector { * Get the case node data from the case directory coordination service node. */ try { - CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); + final CaseNodeData nodeData = CaseNodeData.readCaseNodeData(nodePath); nodeDataList.add(nodeData); } catch (CaseNodeDataException | InterruptedException ex) { logger.log(Level.WARNING, String.format("Error reading case node data from %s", nodePath), ex); From 51d50e5c2bfce689d25838c24ae8df5c9f56ea27 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 18:59:20 -0400 Subject: [PATCH 09/10] Misc polishing for case node data fixes --- .../multiusercases/CaseNodeData.java | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index 10f95a5bce..5c14004a01 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -180,28 +180,29 @@ public final class CaseNodeData { private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { final CaseMetadata metadata = getCaseMetadata(nodePath); CaseNodeData nodeData; - if (oldNodeData.getVersion() == 0) { - /* - * Version 0 node data consisted of only the version number and the - * errors occurred flag and was only written when an auto ingest job - * error occurred. To upgrade from version 0, the version 1 fields - * need to be set from the case metadata and the errors occurred - * flag needs to be carried forward. Note that the last accessed - * date gets advanced to now, since it is otherwise unknown. - */ - nodeData = new CaseNodeData(metadata); - nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); - - } else if (oldNodeData.getVersion() == 1) { - /* - * Version 1 node data did not have a minor version number field. - */ - oldNodeData.setMinorVersion(MINOR_VERSION); - nodeData = oldNodeData; - - } else { - nodeData = oldNodeData; - + switch (oldNodeData.getVersion()) { + case 0: + /* + * Version 0 node data consisted of only the version number and the + * errors occurred flag and was only written when an auto ingest job + * error occurred. To upgrade from version 0, the version 1 fields + * need to be set from the case metadata and the errors occurred + * flag needs to be carried forward. Note that the last accessed + * date gets advanced to now, since it is otherwise unknown. + */ + nodeData = new CaseNodeData(metadata); + nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); + break; + case 1: + /* + * Version 1 node data did not have a minor version number field. + */ + oldNodeData.setMinorVersion(MINOR_VERSION); + nodeData = oldNodeData; + break; + default: + nodeData = oldNodeData; + break; } writeCaseNodeData(nodeData); return nodeData; From 720cb155ac463d2dcf349cd6a9f23c86fc76047d Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Sun, 7 Apr 2019 20:56:55 -0400 Subject: [PATCH 10/10] Misc polishing for case node data fixes --- .../multiusercases/CaseNodeData.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java index 5c14004a01..7a09d737f6 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CaseNodeData.java @@ -178,24 +178,26 @@ public final class CaseNodeData { * @throws CaseMetadataException If the case metadata cannot be read. */ private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException { - final CaseMetadata metadata = getCaseMetadata(nodePath); CaseNodeData nodeData; switch (oldNodeData.getVersion()) { case 0: /* - * Version 0 node data consisted of only the version number and the - * errors occurred flag and was only written when an auto ingest job - * error occurred. To upgrade from version 0, the version 1 fields - * need to be set from the case metadata and the errors occurred - * flag needs to be carried forward. Note that the last accessed - * date gets advanced to now, since it is otherwise unknown. + * Version 0 node data consisted of only the version number and + * the errors occurred flag and was only written when an auto + * ingest job error occurred. To upgrade from version 0, the + * version 1 fields need to be set from the case metadata and + * the errors occurred flag needs to be carried forward. Note + * that the last accessed date gets advanced to now, since it is + * otherwise unknown. */ + final CaseMetadata metadata = getCaseMetadata(nodePath); nodeData = new CaseNodeData(metadata); nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred()); break; case 1: /* - * Version 1 node data did not have a minor version number field. + * Version 1 node data did not have a minor version number + * field. */ oldNodeData.setMinorVersion(MINOR_VERSION); nodeData = oldNodeData;