19package org.sleuthkit.datamodel;
21import com.google.common.annotations.Beta;
22import java.io.FileNotFoundException;
23import java.io.IOException;
24import java.io.RandomAccessFile;
25import java.lang.ref.SoftReference;
26import java.sql.SQLException;
27import java.sql.Statement;
28import java.text.MessageFormat;
29import java.util.ArrayList;
30import java.util.Collection;
31import java.util.Collections;
33import java.util.Objects;
34import java.util.Optional;
35import java.util.ResourceBundle;
37import java.util.SortedSet;
38import java.util.TimeZone;
39import java.util.logging.Level;
40import java.util.logging.Logger;
41import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
42import org.sleuthkit.datamodel.TskData.CollectedStatus;
43import org.sleuthkit.datamodel.TskData.FileKnown;
44import org.sleuthkit.datamodel.TskData.TSK_FS_META_FLAG_ENUM;
45import org.sleuthkit.datamodel.TskData.TSK_FS_META_TYPE_ENUM;
46import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
47import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_TYPE_ENUM;
69 private boolean localPathSet =
false;
70 private String localPath;
71 private String localAbsPath;
72 private volatile RandomAccessFile localFileHandle;
73 private volatile java.io.File localFile;
76 private List<TskFileRange> ranges;
85 private boolean knownStateDirty =
false;
90 private boolean md5HashDirty =
false;
95 private boolean sha256HashDirty =
false;
101 private boolean sha1HashDirty =
false;
104 private boolean collectedDirty =
false;
106 private String mimeType;
107 private boolean mimeTypeDirty =
false;
108 private static final Logger LOGGER = Logger.getLogger(AbstractFile.class.getName());
109 private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(
"org.sleuthkit.datamodel.Bundle");
110 private long dataSourceObjectId;
111 private final String extension;
112 private final List<Attribute> fileAttributesCache =
new ArrayList<Attribute>();
113 private boolean loadedAttributesCacheFromDb =
false;
115 private final String ownerUid;
118 private final Long osAccountObjId;
120 private volatile String uniquePath;
123 private final boolean tryContentProviderStream;
124 private Object contentProviderStreamLock =
new Object();
125 private SoftReference<ContentProviderStream> contentProviderStreamRef =
null;
170 long dataSourceObjectId,
188 Long osAccountObjectId,
190 List<Attribute> fileAttributes) {
191 super(db, objId, name);
192 this.dataSourceObjectId = dataSourceObjectId;
199 this.fileSystemObjectId =
null;
202 this.fileSystemObjectId =
null;
212 this.metaFlags = TSK_FS_META_FLAG_ENUM.valuesOf(
metaFlags);
220 this.modes = TskData.TSK_FS_META_MODE_ENUM.valuesOf(
modes);
226 this.knownState = FileKnown.UNKNOWN;
231 this.mimeType = mimeType;
232 this.extension = extension ==
null ?
"" : extension;
233 this.encodingType = TskData.EncodingType.NONE;
234 this.ownerUid = ownerUid;
235 this.osAccountObjId = osAccountObjectId;
236 this.collected = collected;
239 this.tryContentProviderStream = collected == CollectedStatus.YES_REPO && db.getContentProvider() !=
null;
240 if (Objects.nonNull(fileAttributes) && !fileAttributes.isEmpty()) {
241 this.fileAttributesCache.addAll(fileAttributes);
242 loadedAttributesCacheFromDb =
true;
411 if ((mode & irusr) == irusr) {
416 if ((mode & iwusr) == iwusr) {
423 if ((mode & isuid) == isuid) {
424 if ((mode & ixusr) == ixusr) {
430 if ((mode & ixusr) == ixusr) {
438 if ((mode & irgrp) == irgrp) {
443 if ((mode & iwgrp) == iwgrp) {
450 if ((mode & isgid) == isgid) {
451 if ((mode & ixgrp) == ixgrp) {
457 if ((mode & ixgrp) == ixgrp) {
465 if ((mode & iroth) == iroth) {
470 if ((mode & iwoth) == iwoth) {
477 if ((mode & isvtx) == isvtx) {
478 if ((mode & ixoth) == ixoth) {
484 if ((mode & ixoth) == ixoth) {
492 if (result.length() != 10) {
517 this.mimeType = mimeType;
518 this.mimeTypeDirty =
true;
522 return modes.contains(mode);
535 this.md5HashDirty =
true;
557 this.sha256HashDirty =
true;
566 return this.sha256Hash;
579 this.sha1HashDirty =
true;
588 return this.sha1Hash;
599 synchronized (
this) {
600 if (!loadedAttributesCacheFromDb) {
602 fileAttributesCache.clear();
603 fileAttributesCache.addAll(attributes);
604 loadedAttributesCacheFromDb =
true;
606 return Collections.unmodifiableList(fileAttributesCache);
625 if (Objects.isNull(attributes) || attributes.isEmpty()) {
626 throw new TskCoreException(
"Illegal Argument passed to addAttributes: null or empty attributes passed to addAttributes");
628 boolean isLocalTransaction = Objects.isNull(caseDbTransaction);
630 SleuthkitCase.CaseDbConnection connection = isLocalTransaction ? localTransaction.getConnection() : caseDbTransaction.getConnection();
633 for (
final Attribute attribute : attributes) {
634 attribute.setAttributeParentId(
getId());
639 if (isLocalTransaction) {
640 localTransaction.commit();
641 localTransaction =
null;
644 synchronized (
this) {
645 if (loadedAttributesCacheFromDb) {
646 fileAttributesCache.addAll(attributes);
649 }
catch (SQLException ex) {
650 if (isLocalTransaction &&
null != localTransaction) {
652 localTransaction.rollback();
654 LOGGER.log(Level.SEVERE,
"Failed to rollback transaction after exception", ex2);
673 if (this.knownState.compareTo(
knownState) > 0) {
679 this.knownStateDirty =
true;
744 return dataSourceObjectId;
762 this.collected = collected;
763 collectedDirty =
true;
777 if (ranges ==
null) {
802 long rangeLength = byteRange.getByteLen();
803 if (fileOffset < rangeLength) {
804 imgOffset = byteRange.getByteStart() + fileOffset;
810 fileOffset -= rangeLength;
830 if (fileOffset < 0 || length < 0) {
834 List<TskFileRange> thisRanges =
getRanges();
835 List<TskFileRange> toRet =
new ArrayList<>();
837 long requestedEnd = fileOffset + length;
840 long bytesCounted = 0;
842 for (
int curRangeIdx = 0; curRangeIdx < thisRanges.size(); curRangeIdx++) {
844 if (bytesCounted >= requestedEnd) {
851 long curRangeEnd = bytesCounted + curRangeLen;
856 if (fileOffset < curRangeEnd) {
858 long rangeOffset = Math.max(0, fileOffset - bytesCounted);
861 long newRangeStart = curRange.
getByteStart() + rangeOffset;
864 long rangeOvershoot = Math.max(0, curRangeEnd - requestedEnd);
866 long newRangeLen = curRangeLen - rangeOffset - rangeOvershoot;
867 toRet.add(
new TskFileRange(newRangeStart, newRangeLen, toRet.size()));
870 bytesCounted = curRangeEnd;
952 String[] pathSegments = uniquePath.split(
"/");
956 if (pathSegments[0].startsWith(
"img_")) {
959 if (pathSegments[1].startsWith(
"vol_")) {
965 StringBuilder strbuf =
new StringBuilder();
966 for (; index < pathSegments.length; ++index) {
967 if (!pathSegments[index].isEmpty()) {
968 strbuf.append(
"/").append(pathSegments[index]);
972 return strbuf.toString();
986 List<AbstractFile> files =
new ArrayList<AbstractFile>();
987 for (
Content child : children) {
988 if (child instanceof AbstractFile) {
989 AbstractFile afChild = (AbstractFile) child;
1084 void removeMetaFlag(TSK_FS_META_FLAG_ENUM metaFlag) {
1093 short getMetaFlagsAsInt() {
1094 return TSK_FS_META_FLAG_ENUM.toInt(
metaFlags);
1108 private ContentProviderStream getContentProviderStream() throws TskCoreException {
1109 synchronized (contentProviderStreamLock) {
1111 ContentProviderStream contentProviderStream = contentProviderStreamRef ==
null ? null : contentProviderStreamRef.get();
1113 if (contentProviderStream ==
null) {
1115 contentProviderStream = provider ==
null ? null : provider.
getContentStream(
this).orElse(
null);
1117 if (contentProviderStream ==
null) {
1118 throw new TskCoreException(MessageFormat.format(
"Could not get content provider string for file with obj id: {0}, path: {1}",
1123 this.contentProviderStreamRef =
new SoftReference<>(contentProviderStream);
1126 return contentProviderStream;
1133 if (tryContentProviderStream) {
1135 return contentProviderStream.
read(buf, offset, len);
1136 }
else if (localPathSet) {
1140 return readInt(buf, offset, len);
1172 if (!localPathSet) {
1174 BUNDLE.getString(
"AbstractFile.readLocal.exception.msg1.text"));
1190 if (localFileHandle ==
null) {
1191 synchronized (
this) {
1192 if (localFileHandle ==
null) {
1194 localFileHandle =
new RandomAccessFile(localFile,
"r");
1195 }
catch (FileNotFoundException ex) {
1196 final String msg = MessageFormat.format(BUNDLE.getString(
1197 "AbstractFile.readLocal.exception.msg4.text"),
1199 LOGGER.log(Level.SEVERE, msg, ex);
1211 long encodedOffset = offset + EncodedFileUtil.getHeaderLength();
1214 long curOffset = localFileHandle.getFilePointer();
1215 if (curOffset != encodedOffset) {
1216 localFileHandle.seek(encodedOffset);
1218 bytesRead = localFileHandle.read(buf, 0, (
int) len);
1219 for (
int i = 0; i < bytesRead; i++) {
1220 buf[i] = EncodedFileUtil.decodeByte(buf[i], encodingType);
1225 long curOffset = localFileHandle.getFilePointer();
1226 if (curOffset != offset) {
1227 localFileHandle.seek(offset);
1230 return localFileHandle.read(buf, 0, (
int) len);
1232 }
catch (IOException ex) {
1233 final String msg = MessageFormat.format(BUNDLE.getString(
"AbstractFile.readLocal.exception.msg5.text"), localAbsPath);
1234 LOGGER.log(Level.SEVERE, msg, ex);
1247 void setLocalFilePath(String localPath) {
1249 if (localPath ==
null || localPath.equals(
"")) {
1250 this.localPath =
"";
1251 localAbsPath =
null;
1252 localPathSet =
false;
1257 this.localPath = localPath;
1258 if (
this instanceof DerivedFile) {
1264 if (localPath.startsWith(
"/") || localPath.startsWith(
"\\")
1265 || localPath.matches(
"[A-Za-z]:[/\\\\].*")) {
1266 this.localAbsPath = localPath;
1271 this.localPathSet =
true;
1290 return localAbsPath;
1299 this.encodingType = encodingType;
1310 if (tryContentProviderStream || !localPathSet) {
1315 return localFile.exists();
1317 LOGGER.log(Level.SEVERE, ex.getMessage());
1331 if (tryContentProviderStream || !localPathSet) {
1336 return localFile.canRead();
1338 LOGGER.log(Level.SEVERE, ex.getMessage());
1351 if (!localPathSet) {
1353 BUNDLE.getString(
"AbstractFile.readLocal.exception.msg1.text"));
1357 if (localFile !=
null) {
1361 synchronized (
this) {
1362 if (localFile ==
null) {
1363 localFile =
new java.io.File(localAbsPath);
1372 if (localFileHandle !=
null) {
1373 synchronized (
this) {
1374 if (localFileHandle !=
null) {
1376 localFileHandle.close();
1377 }
catch (IOException ex) {
1378 LOGGER.log(Level.SEVERE,
"Could not close file handle for file: " +
getParentPath() +
getName(), ex);
1380 localFileHandle =
null;
1387 @SuppressWarnings(
"deprecation")
1399 return super.toString(preserveState) +
"AbstractFile [\t"
1401 +
"\tctime " +
ctime
1403 +
"\t" +
"mtime " +
mtime +
"\t" +
"atime " +
atime
1404 +
"\t" +
"attrId " +
attrId
1407 +
"\t" +
"uid " +
uid
1408 +
"\t" +
"gid " +
gid
1413 +
"\t" +
"localPathSet " + localPathSet +
"\t" +
"localPath " + localPath
1414 +
"\t" +
"localAbsPath " + localAbsPath +
"\t" +
"localFile " + localFile
1437 if (this.mimeType ==
null) {
1440 if (mimeTypes.contains(
this.mimeType)) {
1460 if (transaction !=
null) {
1478 if (!(md5HashDirty || sha256HashDirty || sha1HashDirty || mimeTypeDirty || knownStateDirty || collectedDirty)) {
1482 String updateSql =
"";
1483 if (mimeTypeDirty) {
1484 updateSql =
"mime_type = '" + this.
getMIMEType() +
"'";
1487 if (!updateSql.isEmpty()) {
1490 updateSql +=
"md5 = '" + this.
getMd5Hash() +
"'";
1492 if (sha256HashDirty) {
1493 if (!updateSql.isEmpty()) {
1498 if (sha1HashDirty) {
1499 if (!updateSql.isEmpty()) {
1502 updateSql +=
"sha1 = '" + this.
getSha1Hash() +
"'";
1504 if (knownStateDirty) {
1505 if (!updateSql.isEmpty()) {
1508 updateSql +=
"known = '" + this.
getKnown().getFileKnownValue() +
"'";
1510 if (collectedDirty) {
1511 if (!updateSql.isEmpty()) {
1514 updateSql +=
"collected = '" + this.
getCollected().getType() +
"'";
1516 updateSql =
"UPDATE tsk_files SET " + updateSql +
" WHERE obj_id = " + this.
getId();
1518 SleuthkitCase.CaseDbConnection connection = transaction.getConnection();
1519 try (Statement statement = connection.createStatement()) {
1520 connection.executeUpdate(statement, updateSql);
1521 md5HashDirty =
false;
1522 sha256HashDirty =
false;
1523 sha1HashDirty =
false;
1524 mimeTypeDirty =
false;
1525 knownStateDirty =
false;
1526 collectedDirty =
false;
1527 }
catch (SQLException ex) {
1541 return Optional.ofNullable(ownerUid);
1550 return Optional.ofNullable(osAccountObjId);
1559 parentFileSystem = parent;
1595 if (parentFileSystem ==
null) {
1596 synchronized (
this) {
1597 if (parentFileSystem ==
null) {
1602 return parentFileSystem;
1616 if (uniquePath ==
null) {
1622 StringBuilder sb =
new StringBuilder();
1631 uniquePath = sb.toString();
1637 uniquePath = super.getUniquePath();
1643 uniquePath = super.getUniquePath();
1649 String dataSourceName =
"";
1651 if (dataSource !=
null) {
1658 uniquePath = dataSourceName +
"/" +
getName();
1667 @SuppressWarnings(
"deprecation")
1670 return super.newArtifact(artifactTypeID);
1688 return super.newDataArtifact(artifactType, attributesList,
getOsAccountObjectId().orElse(
null));
1700 @SuppressWarnings(
"deprecation")
1727 setLocalFilePath(localPath);
1821 long dataSourceObjectId,
1839 Long osAccountObjectId,
1840 List<Attribute> fileAttributes) {
1841 this(db, objId, dataSourceObjectId,
fileSystemObjectId,
attrType,
attrId, name,
fileType,
metaAddr,
metaSeq,
1842 dirType,
metaType,
dirFlag,
metaFlags,
size,
ctime,
crtime,
atime,
mtime,
modes,
uid,
gid,
AbstractContent(SleuthkitCase db, long obj_id, String name)
List< Content > getChildren()
boolean equals(Object obj)
SleuthkitCase getSleuthkitCase()
TSK_FS_NAME_TYPE_ENUM getDirType()
String getNameExtension()
String getMetaFlagsAsString()
void setMd5Hash(String md5Hash)
final Long fileSystemObjectId
List< AbstractFile > listFiles()
TskData.CollectedStatus getCollected()
TskData.FileKnown knownState
BlackboardArtifact newArtifact(int artifactTypeID)
final int readLocal(byte[] buf, long offset, long len)
TskData.TSK_FS_ATTR_TYPE_ENUM getAttrType()
int countChildrenOfType(List< TSK_FS_NAME_TYPE_ENUM > types)
Set< TSK_FS_META_FLAG_ENUM > metaFlags
long getDataSourceObjectId()
void setMIMEType(String mimeType)
final TSK_FS_NAME_TYPE_ENUM dirType
static String epochToTime(long epoch)
final TSK_FS_META_TYPE_ENUM metaType
String getModesAsString()
void setKnown(TskData.FileKnown knownState)
String getDirFlagAsString()
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributesList)
Optional< Long > getFileSystemObjectId()
abstract boolean isRoot()
TskData.FileKnown getKnown()
String getMetaTypeAsString()
void setSha256Hash(String sha256Hash)
MimeMatchEnum isMimeType(SortedSet< String > mimeTypes)
TSK_FS_NAME_FLAG_ENUM dirFlag
TskData.TSK_DB_FILES_TYPE_ENUM getType()
Optional< String > getOwnerUid()
FileSystem getFileSystem()
boolean isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM flag)
void save(CaseDbTransaction transaction)
final TskData.TSK_FS_ATTR_TYPE_ENUM attrType
boolean isModeSet(TskData.TSK_FS_META_MODE_ENUM mode)
final TskData.TSK_DB_FILES_TYPE_ENUM fileType
void setLocalPath(String localPath, boolean isAbsolute)
Optional< Long > getOsAccountObjectId()
boolean isMetaFlagSet(TSK_FS_META_FLAG_ENUM metaFlag)
long convertToImgOffset(long fileOffset)
List< Attribute > getAttributes()
static long timeToEpoch(String time)
String getDirTypeAsString()
String toString(boolean preserveState)
void addAttributes(Collection< Attribute > attributes, final SleuthkitCase.CaseDbTransaction caseDbTransaction)
void setCollected(TskData.CollectedStatus collected)
static String epochToTime(long epoch, TimeZone tzone)
void setSha1Hash(String sha1Hash)
final Set< TskData.TSK_FS_META_MODE_ENUM > modes
TSK_FS_META_TYPE_ENUM getMetaType()
List< TskFileRange > convertToImgRanges(long fileOffset, long length)
static String createNonUniquePath(String uniquePath)
int readInt(byte[] buf, long offset, long len)
final int read(byte[] buf, long offset, long len)
List< TskFileRange > getRanges()
List< TskFileRange > getFileRanges(long id)
CaseDbTransaction beginTransaction()
Content getContentById(long id)
Blackboard getBlackboard()
static String epochToTime(long epoch)
static long timeToEpoch(String time)
static final String NAME_CARVED
static final String NAME_UNALLOC
TRUE
file does not have a defined mime time in blackboard
FALSE
file has a defined mime type and it is one of the given ones
VIRTUAL_DIR
Virtual directory (not on fs) with no meta-data entry that can be used to group files of types other ...
UNALLOC
Metadata structure is currently in an unallocated state.
ALLOC
Metadata structure is currently in an allocated state.
TSK_FS_META_MODE_ISUID
set user id on execution
TSK_FS_META_MODE_IXGRP
X for group.
TSK_FS_META_MODE_ISGID
set group id on execution
TSK_FS_META_MODE_IXUSR
X for owner.
static short toInt(Set< TSK_FS_META_MODE_ENUM > modes)
TSK_FS_META_MODE_ISVTX
sticky bit
TSK_FS_META_MODE_IROTH
R for other.
TSK_FS_META_MODE_IXOTH
X for other.
TSK_FS_META_MODE_IWOTH
W for other.
TSK_FS_META_MODE_IRGRP
R for group.
TSK_FS_META_MODE_IRUSR
R for owner.
TSK_FS_META_MODE_IWUSR
W for owner.
TSK_FS_META_MODE_IWGRP
W for group.
TSK_FS_META_TYPE_VIRT
"Virtual File" created by TSK for file system areas NON-NLS
TSK_FS_META_TYPE_VIRT_DIR
"Virtual Directory" created by TSK for Orphan Files NON-NLS
TSK_FS_META_TYPE_REG
Regular file NON-NLS.
TSK_FS_META_TYPE_DIR
Directory file NON-NLS.
VIRT
Special (TSK added "Virtual" files) NON-NLS.
int read(byte[] buf, long offset, long len)
Optional< ContentProviderStream > getContentStream(Content content)