Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CaseNodeData.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2017-2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.casemodule.multiusercases;
20 
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.DataInputStream;
24 import java.io.DataOutputStream;
25 import java.io.File;
26 import java.io.IOException;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.text.ParseException;
30 import java.util.Date;
31 import java.util.logging.Level;
37 
41 public final class CaseNodeData {
42 
43  private static final int MAJOR_VERSION = 2;
44  private static final int MINOR_VERSION = 0;
45  private static final Logger logger = Logger.getLogger(CaseNodeData.class.getName());
46 
47  /*
48  * Version 0 fields. Note that version 0 node data was only written to the
49  * coordination service node if an auto ingest job error occurred.
50  */
51  private int version;
52  private boolean errorsOccurred;
53 
54  /*
55  * Version 1 fields.
56  */
57  private Path directory;
58  private Date createDate;
59  private Date lastAccessDate;
60  private String name;
61  private String displayName;
62  private short deletedItemFlags;
63 
64  /*
65  * Version 2 fields.
66  */
67  private int minorVersion;
68 
84  public static CaseNodeData createCaseNodeData(final CaseMetadata metadata) throws CaseNodeDataException, InterruptedException {
85  try {
86  final CaseNodeData nodeData = new CaseNodeData(metadata);
88  return nodeData;
89 
90  } catch (ParseException | IOException | CoordinationServiceException ex) {
91  throw new CaseNodeDataException(String.format("Error creating case node data for coordination service node with path %s", metadata.getCaseDirectory().toUpperCase()), ex); //NON-NLS
92  }
93  }
94 
109  public static CaseNodeData readCaseNodeData(String nodePath) throws CaseNodeDataException, InterruptedException {
110  try {
111  CaseNodeData nodeData;
113  if (nodeBytes != null && nodeBytes.length > 0) {
114  try {
115  nodeData = new CaseNodeData(nodeBytes);
116  } catch (IOException ex) {
117  /*
118  * The existing case node data is corrupted.
119  */
120  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
121  final CaseMetadata metadata = getCaseMetadata(nodePath);
122  nodeData = createCaseNodeData(metadata);
123  logger.log(Level.INFO, String.format("Replaced corrupt node data for coordination service node with path %s", nodePath.toUpperCase())); //NON-NLS
124  }
125  } else {
126  /*
127  * The case node data is missing. Version 0 node data was only
128  * written to the coordination service node if an auto ingest
129  * job error occurred.
130  */
131  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
132  final CaseMetadata metadata = getCaseMetadata(nodePath);
133  nodeData = createCaseNodeData(metadata);
134  logger.log(Level.INFO, String.format("Created node data for coordination service node with path %s", nodePath.toUpperCase())); //NON-NLS
135  }
136  if (nodeData.getVersion() < CaseNodeData.MAJOR_VERSION) {
137  nodeData = upgradeCaseNodeData(nodePath, nodeData);
138  }
139  return nodeData;
140 
141  } catch (CaseNodeDataException | CaseMetadataException | ParseException | IOException | CoordinationServiceException ex) {
142  throw new CaseNodeDataException(String.format("Error reading/writing node data coordination service node with path %s", nodePath.toUpperCase()), ex); //NON-NLS
143  }
144  }
145 
158  public static void writeCaseNodeData(CaseNodeData nodeData) throws CaseNodeDataException, InterruptedException {
159  try {
160  CoordinationService.getInstance().setNodeData(CoordinationService.CategoryNode.CASES, nodeData.getDirectory().toString(), nodeData.toArray());
161 
162  } catch (IOException | CoordinationServiceException ex) {
163  throw new CaseNodeDataException(String.format("Error writing node data coordination service node with path %s", nodeData.getDirectory().toString().toUpperCase()), ex); //NON-NLS
164  }
165  }
166 
180  private static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData) throws CaseNodeDataException, CaseMetadataException, ParseException, IOException, CoordinationServiceException, InterruptedException {
181  CaseNodeData nodeData;
182  switch (oldNodeData.getVersion()) {
183  case 0:
184  /*
185  * Version 0 node data consisted of only the version number and
186  * the errors occurred flag and was only written when an auto
187  * ingest job error occurred. To upgrade from version 0, the
188  * version 1 fields need to be set from the case metadata and
189  * the errors occurred flag needs to be carried forward. Note
190  * that the last accessed date gets advanced to now, since it is
191  * otherwise unknown.
192  */
193  final CaseMetadata metadata = getCaseMetadata(nodePath);
194  nodeData = new CaseNodeData(metadata);
195  nodeData.setErrorsOccurred(oldNodeData.getErrorsOccurred());
196  break;
197  case 1:
198  /*
199  * Version 1 node data did not have a minor version number
200  * field.
201  */
202  oldNodeData.setMinorVersion(MINOR_VERSION);
203  nodeData = oldNodeData;
204  break;
205  default:
206  nodeData = oldNodeData;
207  break;
208  }
209  writeCaseNodeData(nodeData);
210  return nodeData;
211  }
212 
225  private static CaseMetadata getCaseMetadata(String nodePath) throws CaseNodeDataException, CaseMetadataException {
226  final Path caseDirectoryPath = Paths.get(nodePath);
227  final File caseDirectory = caseDirectoryPath.toFile();
228  if (!caseDirectory.exists()) {
229  throw new CaseNodeDataException("Case directory does not exist"); // NON-NLS
230  }
231  final Path metadataFilePath = CaseMetadata.getCaseMetadataFilePath(caseDirectoryPath);
232  if (metadataFilePath == null) {
233  throw new CaseNodeDataException("Case meta data file does not exist"); // NON-NLS
234  }
235  return new CaseMetadata(metadataFilePath);
236  }
237 
247  private CaseNodeData(CaseMetadata metadata) throws ParseException {
248  this.version = MAJOR_VERSION;
249  this.errorsOccurred = false;
250  this.directory = Paths.get(metadata.getCaseDirectory());
251  this.createDate = CaseMetadata.getDateFormat().parse(metadata.getCreatedDate());
252  this.lastAccessDate = new Date();
253  this.name = metadata.getCaseName();
254  this.displayName = metadata.getCaseDisplayName();
255  this.deletedItemFlags = 0;
256  this.minorVersion = MINOR_VERSION;
257  }
258 
267  private CaseNodeData(byte[] nodeData) throws IOException {
268  if (nodeData == null || nodeData.length == 0) {
269  throw new IOException(null == nodeData ? "Null node data byte array" : "Zero-length node data byte array");
270  }
271  try (ByteArrayInputStream byteStream = new ByteArrayInputStream(nodeData); DataInputStream inputStream = new DataInputStream(byteStream)) {
272  this.version = inputStream.readInt();
273  if (this.version == 1) {
274  this.errorsOccurred = inputStream.readBoolean();
275  } else {
276  byte errorsOccurredByte = inputStream.readByte();
277  this.errorsOccurred = (errorsOccurredByte < 0);
278  }
279  if (this.version > 0) {
280  this.directory = Paths.get(inputStream.readUTF());
281  this.createDate = new Date(inputStream.readLong());
282  this.lastAccessDate = new Date(inputStream.readLong());
283  this.name = inputStream.readUTF();
284  this.displayName = inputStream.readUTF();
285  this.deletedItemFlags = inputStream.readShort();
286  }
287  if (this.version > 1) {
288  this.minorVersion = inputStream.readInt();
289  }
290  }
291  }
292 
298  private int getVersion() {
299  return this.version;
300  }
301 
307  private void setMinorVersion(int minorVersion) {
308  this.minorVersion = minorVersion;
309  }
310 
317  public boolean getErrorsOccurred() {
318  return this.errorsOccurred;
319  }
320 
327  public void setErrorsOccurred(boolean errorsOccurred) {
328  this.errorsOccurred = errorsOccurred;
329  }
330 
336  public Path getDirectory() {
337  return this.directory;
338  }
339 
345  public Date getCreateDate() {
346  return new Date(this.createDate.getTime());
347  }
348 
354  public Date getLastAccessDate() {
355  return new Date(this.lastAccessDate.getTime());
356  }
357 
363  public void setLastAccessDate(Date lastAccessDate) {
364  this.lastAccessDate = new Date(lastAccessDate.getTime());
365  }
366 
372  public String getName() {
373  return this.name;
374  }
375 
381  public String getDisplayName() {
382  return this.displayName;
383  }
384 
390  public void setDisplayName(String displayName) {
391  this.displayName = displayName;
392  }
393 
401  public boolean isDeletedFlagSet(DeletedFlags flag) {
402  return (this.deletedItemFlags & flag.getValue()) == flag.getValue();
403  }
404 
410  public void setDeletedFlag(DeletedFlags flag) {
411  this.deletedItemFlags |= flag.getValue();
412  }
413 
423  private byte[] toArray() throws IOException {
424  try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream outputStream = new DataOutputStream(byteStream)) {
425  outputStream.writeInt(this.version);
426  outputStream.writeByte((byte) (this.errorsOccurred ? 0x80 : 0));
427  outputStream.writeUTF(this.directory.toString());
428  outputStream.writeLong(this.createDate.getTime());
429  outputStream.writeLong(this.lastAccessDate.getTime());
430  outputStream.writeUTF(this.name);
431  outputStream.writeUTF(this.displayName);
432  outputStream.writeShort(this.deletedItemFlags);
433  outputStream.writeInt(this.minorVersion);
434  outputStream.flush();
435  byteStream.flush();
436  return byteStream.toByteArray();
437  }
438  }
439 
443  public enum DeletedFlags {
444 
450 
451  private final short value;
452 
458  private DeletedFlags(int value) {
459  this.value = (short) value;
460  }
461 
467  private short getValue() {
468  return value;
469  }
470 
471  }
472 
477  public static final class CaseNodeDataException extends Exception {
478 
479  private static final long serialVersionUID = 1L;
480 
487  private CaseNodeDataException(String message) {
488  super(message);
489  }
490 
498  private CaseNodeDataException(String message, Throwable cause) {
499  super(message, cause);
500  }
501  }
502 
503 }
static CaseNodeData createCaseNodeData(final CaseMetadata metadata)
static Path getCaseMetadataFilePath(Path directoryPath)
byte[] getNodeData(CategoryNode category, String nodePath)
static CaseNodeData upgradeCaseNodeData(String nodePath, CaseNodeData oldNodeData)
void setNodeData(CategoryNode category, String nodePath, byte[] data)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.