diff --git a/.gitignore b/.gitignore index 65e597955bd834de82e3ed34dd1ce2438f070ad0..6dedcd0144a08e36bffd5295c4cd79c39d057a68 100755 --- a/.gitignore +++ b/.gitignore @@ -1,128 +1,139 @@ -# NetBeans user-specific settings -/bindings/java/nbproject/private/ - -# Bindings dependecies and build folders -/bindings/java/lib/ -/bindings/java/build/ -/bindings/java/dist -/bindings/java/test/output/results -/bindings/java/test/output/gold/dummy -/bindings/java/test/output/gold/*_BU.txt -/bindings/java/test/output/gold/*_CPP.txt -/bindings/java/test/output/gold/*_CPP_SRT.txt -/bindings/java/test/input -/bindings/java/nbproject/genfiles.properties -/bindings/java/nbproject/nbjdk.properties -/bindings/java/nbproject/jdk.xml -/bindings/java/nbproject/nbjdk.xml -/bindings/java/libts* -*~ -*.class -/bindings/java/build/ -/bindings/java/dist/ -/bindings/java/nbproject/* -!/bindings/java/nbproject/project.xml -!/bindings/java/nbproject/project.properties - -# Windows build folders -/win32/Debug_NoLibs/ -/win32/*/Debug_NoLibs/ -/win32/Debug/ -/win32/*/Debug/ -/win32/Release/ -/win32/*/Release/ -/win32/*/*.user -win32/ipch -win32/tsk-win.opensdf -framework/msvcpp/framework/Debug/ -framework/msvcpp/framework/Release/ -framework/msvcpp/*/*.user -framework/msvcpp/*/Debug/ -framework/msvcpp/*/Release/ -framework/msvcpp/BuildLog.txt -framework/msvcpp/*/ipch -framework/runtime/ -framework/SampleConfig/to_install/ - -# IntelliSense data -/win32/*.ncb -/win32/*.sdf -framework/msvcpp/framework/*.ncb -framework/msvcpp/framework/*sdf - -# Visual Studio user options -/win32/tsk-win.suo -framework/msvcpp/framework/*.suo -*.sln.cache - -# Make crud -*.o -*.lo -*.la -*.jar -Makefile -.deps -.libs - -#javadoc generated -/bindings/java/javadoc - -# Files generated by running configure -*.in -stamp-h1 -tsk/tsk_config.h -tsk/tsk_incs.h -tools/fstools/blkcalc -aclocal.m4 -autom4te.cache -config.log -config.status -configure -libtool -m4/libtool.m4 -m4/lt*.m4 -config/* - - -# Executables -samples/callback_cpp_style -samples/callback_style -samples/posix_cpp_style -samples/posix_style -tests/fs_attrlist_apis -tests/fs_fname_apis -tests/fs_thread_test -tests/read_apis -tools/autotools/tsk_comparedir -tools/autotools/tsk_gettimes -tools/autotools/tsk_loaddb -tools/autotools/tsk_recover -tools/fiwalk/plugins/jpeg_extract -tools/fiwalk/src/fiwalk -tools/fiwalk/src/test_arff -tools/fstools/blkcat -tools/fstools/blkls -tools/fstools/blkstat -tools/fstools/fcat -tools/fstools/ffind -tools/fstools/fls -tools/fstools/fsstat -tools/fstools/icat -tools/fstools/ifind -tools/fstools/ils -tools/fstools/istat -tools/fstools/jcat -tools/fstools/jls -tools/hashtools/hfind -tools/imgtools/img_cat -tools/imgtools/img_stat -tools/sorter/sorter -tools/srchtools/sigfind -tools/srchtools/srch_strings -tools/timeline/mactime -tools/vstools/mmcat -tools/vstools/mmls -tools/vstools/mmstat - -# EMACS backup files -*~ +# NetBeans user-specific settings +/bindings/java/nbproject/private/ + +# Bindings dependecies and build folders +/bindings/java/lib/ +/bindings/java/build/ +/bindings/java/dist +/bindings/java/test/output/results +/bindings/java/test/output/gold/dummy +/bindings/java/test/output/gold/*_BU.txt +/bindings/java/test/output/gold/*_CPP.txt +/bindings/java/test/output/gold/*_CPP_SRT.txt +/bindings/java/test/input +/bindings/java/nbproject/genfiles.properties +/bindings/java/nbproject/nbjdk.properties +/bindings/java/nbproject/jdk.xml +/bindings/java/nbproject/nbjdk.xml +/bindings/java/libts* +*~ +*.class +/bindings/java/build/ +/bindings/java/dist/ +/bindings/java/nbproject/* +!/bindings/java/nbproject/project.xml +!/bindings/java/nbproject/project.properties + +# Windows build folders +/win32/Debug_NoLibs/ +/win32/*/Debug_NoLibs/ +/win32/Debug/ +/win32/*/Debug/ +/win32/Release/ +/win32/*/Release/ +/win32/*/*.user +win32/ipch +win32/BuildErrors.txt +framework/msvcpp/framework/Debug/ +framework/msvcpp/framework/Release/ +framework/msvcpp/*/*.user +framework/msvcpp/*/Debug/ +framework/msvcpp/*/Release/ +framework/msvcpp/BuildLog.txt +framework/msvcpp/*/ipch +framework/runtime/ +framework/SampleConfig/to_install/ +framework/modules/*/win32/Debug/ +framework/modules/*/win32/Release/ +framework/modules/*/win32/*.user +framework/modules/c_InterestingFilesModule/tsk + +# Release files +release/sleuthkit-* + +# IntelliSense data +/win32/*.ncb +/win32/*.sdf +framework/msvcpp/framework/*.ncb +framework/msvcpp/framework/*sdf + +# Visual Studio user options +/win32/tsk-win.suo +framework/msvcpp/framework/*.suo +*.sln.cache + +# Make crud +*.o +*.lo +*.la +*.jar +Makefile +.deps +.libs + +#javadoc generated +/bindings/java/javadoc + +# Files generated by running configure +*.in +stamp-h1 +tsk/tsk_config.h +tsk/tsk_incs.h +tools/fstools/blkcalc +aclocal.m4 +autom4te.cache +config.log +config.status +configure +libtool +m4/libtool.m4 +m4/lt*.m4 +config/* + + +# Executables +samples/callback_cpp_style +samples/callback_style +samples/posix_cpp_style +samples/posix_style +samples/*.exe +tests/fs_attrlist_apis +tests/fs_fname_apis +tests/fs_thread_test +tests/read_apis +tests/*.exe +tools/autotools/tsk_comparedir +tools/autotools/tsk_gettimes +tools/autotools/tsk_loaddb +tools/autotools/tsk_recover +tools/fiwalk/plugins/jpeg_extract +tools/fiwalk/src/fiwalk +tools/fiwalk/src/test_arff +tools/fstools/blkcat +tools/fstools/blkls +tools/fstools/blkstat +tools/fstools/fcat +tools/fstools/ffind +tools/fstools/fls +tools/fstools/fsstat +tools/fstools/icat +tools/fstools/ifind +tools/fstools/ils +tools/fstools/istat +tools/fstools/jcat +tools/fstools/jls +tools/hashtools/hfind +tools/imgtools/img_cat +tools/imgtools/img_stat +tools/sorter/sorter +tools/srchtools/sigfind +tools/srchtools/srch_strings +tools/timeline/mactime +tools/vstools/mmcat +tools/vstools/mmls +tools/vstools/mmstat +tools/*/*.exe +tools/*/*/*.exe + +# EMACS backup files +*~ \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index a3f2f2c1e6655249867b8f8801083069399815ac..33390d34bbf3ed82f0ef5f3d0cd8ef71943d311b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -61,10 +61,14 @@ ACLOCAL_AMFLAGS = -I m4 if CPPUNIT UNIT_TESTS=unit_tests endif + if X_JNI - JNI=bindings/java/jni + JAVA_BINDINGS=bindings/java/jni bindings/java +else + JAVA_BINDINGS= endif -SUBDIRS = tsk tools tests samples man $(UNIT_TESTS) $(JNI) + +SUBDIRS = tsk tools tests samples man $(UNIT_TESTS) $(JAVA_BINDINGS) nobase_include_HEADERS = tsk/libtsk.h tsk/tsk_incs.h \ tsk/base/tsk_base.h tsk/base/tsk_os.h \ diff --git a/bindings/java/Makefile.am b/bindings/java/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..fa70d5ca8236a1b6e1c426022d1b5c7dbfd5db9d --- /dev/null +++ b/bindings/java/Makefile.am @@ -0,0 +1,24 @@ +Z_PATH=@Z_PATH@ +EWF_PATH=@EWF_PATH@ +ANT_PROPS= + +if X_ZLIB + ANT_PROPS+=-Dlib.z.path=$(Z_PATH) +endif + +if X_LIBEWF + ANT_PROPS+=-Dlib.ewf.path=$(EWF_PATH) +endif + +tsk_jar = $(top_builddir)/bindings/java/dist/Tsk_DataModel.jar +jardir = $(prefix)/share/java +jar_DATA = $(tsk_jar) + + +$(tsk_jar): + ant dist $(ANT_PROPS) + +CLEANFILES = $(tsk_jar) + +clean-local: + ant clean diff --git a/bindings/java/build-unix.xml b/bindings/java/build-unix.xml index f1451406e997aa20fa597b9ae4fc8433ead1f7ac..81edd272efaf026818a45c2597e07c798e7624e0 100644 --- a/bindings/java/build-unix.xml +++ b/bindings/java/build-unix.xml @@ -30,6 +30,9 @@ <available file="./jni/.libs/libtsk_jni.so" property="tsk_so.present"/> <available file="./jni/.libs/libtsk_jni.a" property="present"/> <fail unless="present" message="Run make install on The Sleuthkit."/> + <!-- Default location to find zlib and libewf. Overwritten by properties in makefile --> + <property name="lib.z.path" value="/usr/lib"/> + <property name="lib.ewf.path" value="/usr/local/lib"/> </target> <!-- OS X --> @@ -42,16 +45,16 @@ <property environment="env"/> <property name="jni.dylib" location="${basedir}/jni/.libs/libtsk_jni.dylib" /> <property name="jni.jnilib" value="libtsk_jni.jnilib" /> - <property name="zlib.jni" location="/usr/lib/libz.dylib"/> - <property name="libewf.jni" location="/usr/local/lib/libewf.dylib"/> + <property name="zlib.jni" location="${lib.z.path}/libz.dylib"/> + <property name="libewf.jni" location="${lib.ewf.path}/libewf.dylib"/> <!-- x86_64 --> <copy file="${jni.dylib}" tofile="${x86_64}/mac/${jni.jnilib}"/> <copy file="${zlib.jni}" tofile="${x86_64}/mac/zlib.dylib"/> <copy file="${libewf.jni}" tofile="${x86_64}/mac/libewf.dylib"/> <!-- amd64 --> <copy file="${jni.dylib}" tofile="${amd64}/mac/${jni.jnilib}"/> - <copy file="${zlib.jni}" tofile="${x86_64}/mac/zlib.dylib"/> - <copy file="${libewf.jni}" tofile="${x86_64}/mac/libewf.dylib"/> + <copy file="${zlib.jni}" tofile="${amd64}/mac/zlib.dylib"/> + <copy file="${libewf.jni}" tofile="${amd64}/mac/libewf.dylib"/> </target> <!-- Non-OS X --> @@ -63,18 +66,32 @@ <target name="copyLinuxLibs" depends="testTSKLibs" if="tsk_so.present"> <property environment="env"/> <property name="jni.so" location="${basedir}/jni/.libs/libtsk_jni.so" /> + <property name="zlib.so" location="${lib.z.path}/libz.so"/> + <property name="libewf.so" location="${lib.ewf.path}/libewf.so"/> <!-- x86_64 --> <copy file="${jni.so}" tofile="${x86_64}/linux/libtsk_jni.so"/> + <copy file="${zlib.so}" tofile="${x86_64}/linux/libz.so"/> + <copy file="${libewf.so}" tofile="${x86_64}/linux/libewf.so"/> <!-- amd64 --> <copy file="${jni.so}" tofile="${amd64}/linux/libtsk_jni.so"/> + <copy file="${zlib.so}" tofile="${amd64}/linux/libz.so"/> + <copy file="${libewf.so}" tofile="${amd64}/linux/libewf.so"/> <!-- x86 --> <copy file="${jni.so}" tofile="${x86}/linux/libtsk_jni.so"/> + <copy file="${zlib.so}" tofile="${x86}/linux/libz.so"/> + <copy file="${libewf.so}" tofile="${x86}/linux/libewf.so"/> <!-- i386 --> <copy file="${jni.so}" tofile="${i386}/linux/libtsk_jni.so"/> + <copy file="${zlib.so}" tofile="${i386}/linux/libz.so"/> + <copy file="${libewf.so}" tofile="${i386}/linux/libewf.so"/> <!-- i586 --> <copy file="${jni.so}" tofile="${i586}/linux/libtsk_jni.so"/> + <copy file="${zlib.so}" tofile="${i586}/linux/libz.so"/> + <copy file="${libewf.so}" tofile="${i586}/linux/libewf.so"/> <!-- i686 --> <copy file="${jni.so}" tofile="${i686}/linux/libtsk_jni.so"/> + <copy file="${zlib.so}" tofile="${i686}/linux/libz.so"/> + <copy file="${libewf.so}" tofile="${i686}/linux/libewf.so"/> </target> <target name="copyLibs" depends="copyLinuxLibs,copyMacLibs" /> diff --git a/bindings/java/src/org/sleuthkit/datamodel/LibraryUtils.java b/bindings/java/src/org/sleuthkit/datamodel/LibraryUtils.java index dd7e506dfec352291f7cbccc0c340a4988a84831..d56ae1b52309e6d3ebd60d6c1baddf6627de180b 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/LibraryUtils.java +++ b/bindings/java/src/org/sleuthkit/datamodel/LibraryUtils.java @@ -77,34 +77,40 @@ public static boolean loadAuxilliaryLibs() { if (LibraryUtils.isWindows()) { loaded = LibraryUtils.loadCRTLibs(); } - - if (! LibraryUtils.isLinux()) { - - for(LibraryUtils.Lib lib : LibraryUtils.getLibs()) { - loaded = LibraryUtils.loadLibFromJar(lib); - if (!loaded) { - System.out.println("SleuthkitJNI: failed to load " + lib.getLibName()); - } else { - System.out.println("SleuthkitJNI: loaded " + lib.getLibName()); - } + + // Always try to load from jar first. + for(Lib lib : LibraryUtils.getLibs()) { + // Always try to load from jar first. + loaded = LibraryUtils.loadLibFromJar(lib); + if (!loaded) { + // if that fails, try to load from system + loaded = loadLibFromSystem(lib); } - } else { - System.out.println("In unix path."); - // Unix platform - for (Lib lib : LibraryUtils.getLibs()) { - try { - System.out.println("Lib name: " + lib.getUnixName()); - System.loadLibrary(lib.getUnixName()); - System.out.println("SleuthkitJNI: loaded " + lib.getLibName()); - } catch (UnsatisfiedLinkError e) { - loaded = false; - System.out.println("SleuthkitJNI: failed to load " + lib.getLibName()); - } + if (!loaded) { + System.out.println("SleuthkitJNI: failed to load " + lib.getLibName()); + } else { + System.out.println("SleuthkitJNI: loaded " + lib.getLibName()); } } return loaded; } + /** + * Try to load the given Library from the System path. + * + * @param lib + * @return + */ + private static boolean loadLibFromSystem(Lib lib) { + String libName = (isWindows() ? lib.getLibName() : lib.getUnixName()); + try { + System.loadLibrary(libName); + } catch (UnsatisfiedLinkError e) { + return false; + } + return true; + } + /** * Load the Sleuthkit JNI. * @@ -167,6 +173,8 @@ private static String getPlatform() { os = "win"; } else if(LibraryUtils.isMac()) { os = "mac"; + } else if(LibraryUtils.isLinux()) { + os = "linux"; } // os.arch represents the architecture of the JVM, not the os String arch = System.getProperty("os.arch"); @@ -232,7 +240,7 @@ private static boolean loadLibFromJar(Lib library) { // copy library to temp folder and load it try { - java.io.File libTemp = new java.io.File(System.getProperty("java.io.tmpdir") + libName + libExt); + java.io.File libTemp = new java.io.File(System.getProperty("java.io.tmpdir") + java.io.File.separator + libName + libExt); if(libTemp.exists()) { // Delete old file diff --git a/bindings/java/src/org/sleuthkit/datamodel/TskData.java b/bindings/java/src/org/sleuthkit/datamodel/TskData.java index 971e3de0682dc46e5a307f59e6d62150c2c5bd14..7c214dbf50a6f428882035d7b28377bfac4d6bb4 100644 --- a/bindings/java/src/org/sleuthkit/datamodel/TskData.java +++ b/bindings/java/src/org/sleuthkit/datamodel/TskData.java @@ -413,8 +413,8 @@ public enum TSK_FS_TYPE_ENUM { TSK_FS_TYPE_HFS (0x00001000), ///< HFS file system TSK_FS_TYPE_HFS_DETECT (0x00001000), ///< HFS auto detection TSK_FS_TYPE_EXT4 (0x00002000), ///< Ext4 file system - TSK_FS_TYPE_YAFFS2(0x00003000), ///< YAFFS2 file system - TSK_FS_TYPE_YAFFS2_DETECT(0x00003000), ///< YAFFS2 auto detection + TSK_FS_TYPE_YAFFS2(0x00004000), ///< YAFFS2 file system + TSK_FS_TYPE_YAFFS2_DETECT(0x00004000), ///< YAFFS2 auto detection TSK_FS_TYPE_UNSUPP (0xffffffff); ///< Unsupported file system private int value; diff --git a/configure.ac b/configure.ac index 64ba753c7ff04a52c939b01d1913632ad6c06531..f1d6d10d26ca73f50215d3beb1ff70eb8c576668 100644 --- a/configure.ac +++ b/configure.ac @@ -11,7 +11,8 @@ m4_include([m4/cppunit.m4]) m4_include([m4/ax_jni_include_dir.m4]) m4_include([m4/ac_prog_javac_works.m4]) m4_include([m4/ac_prog_javac.m4]) - +m4_include([m4/ac_prog_java_works.m4]) +m4_include([m4/ac_prog_java.m4]) AC_CONFIG_SRCDIR([tsk/base/tsk_base.h]) AC_CONFIG_HEADERS([tsk/tsk_config.h]) @@ -87,6 +88,10 @@ if test -d /usr/local/include; then LDFLAGS="$LDFLAGS -L/usr/local/lib" fi +dnl Add enable/disable option +AC_ARG_ENABLE([java], + [AS_HELP_STRING([--disable-java], [Do not build the java bindings or jar file])]) + dnl Checks for libraries. @@ -173,6 +178,13 @@ AS_IF( ) AS_IF([test "x$ac_cv_lib_z_inflate" = "xyes"], [ax_zlib=yes], [ax_zlib=no]) +AM_CONDITIONAL([X_ZLIB],[test "x$with_zlib" != "xno" && test "x$with_zlib" != "xyes"]) +AS_IF([test "x$with_zlib" != "xno"], + [Z_PATH="${with_zlib}/lib"], + [AC_MSG_NOTICE([failed to make Z_PATH])] +) +AC_SUBST(Z_PATH, $Z_PATH) + dnl needed for sqllite AC_CHECK_LIB(dl, dlopen) @@ -198,25 +210,41 @@ AS_IF([test "x$with_libewf" != "xno"], )] dnl Check for the header file first to make sure they have the dev install [AC_CHECK_HEADERS([libewf.h], - [AC_CHECK_LIB([ewf], [libewf_get_version])] + [AC_CHECK_LIB([ewf], [libewf_get_version], [], [NO_LIBEWF=true])] )] ) AS_IF([test "x$ac_cv_lib_ewf_libewf_get_version" = "xyes"], [ax_libewf=yes], [ax_libewf=no]) +AM_CONDITIONAL([X_LIBEWF],[test "x$with_libewf" != "xno" && test "x$with_libewf" != "xyes"]) +AS_IF([test "x$with_libewf" != "xno"], + [EWF_PATH="${with_libewf}/lib"], +) +AC_SUBST(EWF_PATH, $EWF_PATH) + +dnl sqlite requires pthread libraries - this was copied from its configure.ac +dnl AC_SEARCH_LIBS(pthread_create, pthread) +AC_SEARCH_LIBS(dlopen, dl) + dnl Test for java/jni so that we can compile the java bindings -AC_PROG_JAVAC -if test "x$JAVAC" != x; then - AX_JNI_INCLUDE_DIR - for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS - do - JNI_CPPFLAGS="$JNI_CPPFLAGS -I$JNI_INCLUDE_DIR" - done - dnl Export the paths so that the makefile gets them - AC_SUBST(JNI_CPPFLAGS, $JNI_CPPFLAGS) -fi -AM_CONDITIONAL([X_JNI],[test "x$JNI_CPPFLAGS" != x]) +AS_IF([test "x$enable_java" != "xno"], [ + AC_PROG_JAVAC + if test "x$JAVAC" != x; then + AX_JNI_INCLUDE_DIR + for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS + do + JNI_CPPFLAGS="$JNI_CPPFLAGS -I$JNI_INCLUDE_DIR" + done + dnl Export the paths so that the makefile gets them + AC_SUBST(JNI_CPPFLAGS, $JNI_CPPFLAGS) + fi + AC_PROG_JAVA +]) dnl test enable_java + +dnl Test is ant is available +AC_PATH_PROG([ANT_FOUND], [ant], []) +AM_CONDITIONAL([X_JNI],[test "x$JNI_CPPFLAGS" != x && test "x$ANT_FOUND" != x && test "x$JAVA" != x]) -AS_IF([test "x$JNI_CPPFLAGS" != x], [ax_java_support=yes], [ax_java_support=no]) +AS_IF([test "x$JNI_CPPFLAGS" != x && test "x$ANT_FOUND" != x && test "x$JAVA" != x], [ax_java_support=yes], [ax_java_support=no]) AC_CONFIG_COMMANDS([tsk/tsk_incs.h], [echo "#ifndef _TSK_INCS_H" > tsk/tsk_incs.h @@ -274,6 +302,7 @@ AC_CONFIG_FILES([ tests/Makefile samples/Makefile man/Makefile + bindings/java/Makefile bindings/java/jni/Makefile unit_tests/Makefile unit_tests/base/Makefile]) diff --git a/framework/modules/c_InterestingFilesModule/InterestingFilesModule.cpp b/framework/modules/c_InterestingFilesModule/InterestingFilesModule.cpp index be6e0f1013c2003f513227e2a5206d9079ae9746..81435a3e34de9a519473715e9135add9f2bbe6af 100644 --- a/framework/modules/c_InterestingFilesModule/InterestingFilesModule.cpp +++ b/framework/modules/c_InterestingFilesModule/InterestingFilesModule.cpp @@ -22,6 +22,7 @@ #include "Poco/AutoPtr.h" #include "Poco/Path.h" #include "Poco/File.h" +#include "Poco/NumberParser.h" #include "Poco/DOM/DOMParser.h" #include "Poco/DOM/Document.h" #include "Poco/DOM/NodeList.h" @@ -45,6 +46,7 @@ namespace const std::string INTERESTING_FILE_SET_ELEMENT_TAG = "INTERESTING_FILE_SET"; const std::string NAME_ATTRIBUTE = "name"; const std::string DESCRIPTION_ATTRIBUTE_TAG = "description"; + const std::string IGNORE_KNOWN_TAG = "ignoreKnown"; const std::string NAME_ELEMENT_TAG = "NAME"; const std::string EXTENSION_ELEMENT_TAG = "EXTENSION"; const std::string PATH_FILTER_ATTRIBUTE = "pathFilter"; @@ -54,6 +56,17 @@ namespace std::string configFilePath; + // The following variables track whether we should ignore known + // files (and what type of known files to ignore) at a global + // level. These settings can be overridden on an individual + // file set. + + // Whether we should ignore known files. + bool ignoreKnown = false; + + // What type of known files to ignore. + TskImgDB::KNOWN_STATUS knownType = TskImgDB::IMGDB_FILES_KNOWN; + /** * An interesting files set is defined by a set name, a set description, * and one or more SQL WHERE clauses that specify what files belong to the @@ -61,9 +74,12 @@ namespace */ struct InterestingFilesSet { - InterestingFilesSet() : name(""), description("") {} + InterestingFilesSet() + : name(""), description(""), ignoreKnown(false), knownType(TskImgDB::IMGDB_FILES_KNOWN) {} std::string name; std::string description; + bool ignoreKnown; + TskImgDB::KNOWN_STATUS knownType; vector<std::string> conditions; }; @@ -113,6 +129,25 @@ namespace Poco::replaceInPlace(stringToChange, "*", "%"); } + /** + * Verifies that the given attribute value is a valid integer value + * for a known type and converts the value to its corresponding enum. + */ + TskImgDB::KNOWN_STATUS parseKnownType(const std::string& attributeValue) + { + const std::string MSG_PREFIX(MODULE_NAME + std::string("::parseKnownType : ")); + + int knownType = Poco::NumberParser::parse(attributeValue); + if (knownType > TskImgDB::IMGDB_FILES_UNKNOWN || knownType < TskImgDB::IMGDB_FILES_KNOWN) + { + std::ostringstream msg; + msg << MSG_PREFIX << "Invalid value for ignoreKnown."; + throw TskException(msg.str()); + } + + return static_cast<TskImgDB::KNOWN_STATUS>(knownType); + } + /** * Adds optional file type (file, directory) and path substring filters to * an SQL WHERE clause for a file search condition. @@ -188,13 +223,12 @@ namespace } /** - * Creates an SQL WHERE clause for a file query from a file name - * condition. + * Creates an SQL condition to find files based on file name. * * @param conditionDefinition A file name condition XML element. - * @param conditions The WHERE clause is added to this collection. + * @return The constructed SQL condition. */ - void compileFileNameSearchCondition(const Poco::XML::Node *conditionDefinition, std::vector<std::string> &conditions) + std::string compileFileNameSearchCondition(const Poco::XML::Node *conditionDefinition) { const std::string MSG_PREFIX = "InterestingFilesModule::compileFileNameSearchCondition : "; @@ -207,29 +241,29 @@ namespace } std::stringstream conditionBuilder; + if (hasGlobWildcards(name)) { convertGlobWildcardsToSQLWildcards(name); - conditionBuilder << "WHERE UPPER(name) LIKE UPPER(" << TskServices::Instance().getImgDB().quote(name) << ") ESCAPE '#' "; + conditionBuilder << "UPPER(name) LIKE UPPER(" << TskServices::Instance().getImgDB().quote(name) << ") ESCAPE '#' "; } else { - conditionBuilder << "WHERE UPPER(name) = UPPER(" + TskServices::Instance().getImgDB().quote(name) + ")"; + conditionBuilder << "UPPER(name) = UPPER(" + TskServices::Instance().getImgDB().quote(name) + ")"; } addPathAndTypeFilterOptions(conditionDefinition, conditionBuilder); - conditionBuilder << " ORDER BY file_id"; - conditions.push_back(conditionBuilder.str()); + + return conditionBuilder.str(); } /** - * Creates an SQL WHERE clause for a file query from a file extension - * condition. + * Creates an SQL condition to find files based on extension. * * @param conditionDefinition A file extension condition XML element. - * @param conditions The WHERE clause is added to this collection. + * @returns The SQL condition. */ - void compileExtensionSearchCondition(const Poco::XML::Node *conditionDefinition, std::vector<std::string> &conditions) + std::string compileExtensionSearchCondition(const Poco::XML::Node *conditionDefinition) { const std::string MSG_PREFIX = "InterestingFilesModule::compileExtensionSearchCondition : "; @@ -253,11 +287,11 @@ namespace // @@@ TODO: In combination with glob wildcards this may create some unxepected matches. // For example, ".htm*" will become "%.htm%" which will match "file.htm.txt" and the like. std::stringstream conditionBuilder; - conditionBuilder << "WHERE UPPER(name) LIKE UPPER('%" << extension << "') ESCAPE '#' "; + conditionBuilder << "UPPER(name) LIKE UPPER('%" << extension << "') ESCAPE '#' "; + + addPathAndTypeFilterOptions(conditionDefinition, conditionBuilder); - addPathAndTypeFilterOptions(conditionDefinition, conditionBuilder); - conditionBuilder << " ORDER BY file_id"; - conditions.push_back(conditionBuilder.str()); + return conditionBuilder.str(); } /** @@ -314,6 +348,20 @@ namespace LOGWARN(msg.str()); } } + else if (attributeName == IGNORE_KNOWN_TAG) + { + if (!attributeValue.empty()) + { + fileSet.knownType = parseKnownType(attributeValue); + fileSet.ignoreKnown = true; + } + else + { + std::ostringstream msg; + msg << MSG_PREFIX << "ignored " << INTERESTING_FILE_SET_ELEMENT_TAG << "'" << IGNORE_KNOWN_TAG << "' attribute without a value"; + LOGWARN(msg.str()); + } + } else { std::ostringstream msg; @@ -358,6 +406,15 @@ namespace throw TskException(msg.str()); } + std::string conditionBase; + + // If we want to ignore known files either on an individual file set + // or globally we need to join with the file_hashes table. + if (fileSet.ignoreKnown || ignoreKnown) + conditionBase = " JOIN file_hashes ON (files.file_id = file_hashes.file_id) WHERE "; + else + conditionBase = " WHERE "; + // Get the search conditions. Poco::AutoPtr<Poco::XML::NodeList>conditionDefinitions = fileSetDefinition->childNodes(); for (unsigned long i = 0; i < conditionDefinitions->length(); ++i) @@ -366,13 +423,17 @@ namespace if (conditionDefinition->nodeType() == Poco::XML::Node::ELEMENT_NODE) { const std::string &conditionType = Poco::XML::fromXMLString(conditionDefinition->nodeName()); + std::stringstream conditionBuilder; + + conditionBuilder << conditionBase; + if (conditionType == NAME_ELEMENT_TAG) { - compileFileNameSearchCondition(conditionDefinition, fileSet.conditions); + conditionBuilder << compileFileNameSearchCondition(conditionDefinition); } else if (conditionType == EXTENSION_ELEMENT_TAG) { - compileExtensionSearchCondition(conditionDefinition, fileSet.conditions); + conditionBuilder << compileExtensionSearchCondition(conditionDefinition); } else { @@ -380,6 +441,14 @@ namespace msg << MSG_PREFIX << "unrecognized " << INTERESTING_FILE_SET_ELEMENT_TAG << " child element '" << conditionType << "'"; throw TskException(msg.str()); } + + if (fileSet.ignoreKnown) + conditionBuilder << " AND file_hashes.known != " << fileSet.knownType; + else if (ignoreKnown) + conditionBuilder << " AND file_hashes.known != " << knownType; + + conditionBuilder << " ORDER BY files.file_id"; + fileSet.conditions.push_back(conditionBuilder.str()); } } @@ -468,6 +537,23 @@ extern "C" { Poco::XML::InputSource inputSource(configStream); Poco::AutoPtr<Poco::XML::Document> configDoc = Poco::XML::DOMParser().parse(&inputSource); + + Poco::XML::Element * rootElement = configDoc->documentElement(); + if (rootElement == NULL) + { + std::ostringstream msg; + msg << MSG_PREFIX << "Root element of config file is NULL."; + throw TskException(msg.str()); + } + + const std::string& ignoreKnownValue = Poco::XML::fromXMLString(rootElement->getAttribute(IGNORE_KNOWN_TAG)); + + if (!ignoreKnownValue.empty()) + { + knownType = parseKnownType(ignoreKnownValue); + ignoreKnown = true; + } + Poco::AutoPtr<Poco::XML::NodeList> fileSetDefinitions = configDoc->getElementsByTagName(INTERESTING_FILE_SET_ELEMENT_TAG); for (unsigned long i = 0; i < fileSetDefinitions->length(); ++i) { diff --git a/framework/modules/c_InterestingFilesModule/README.txt b/framework/modules/c_InterestingFilesModule/README.txt index 9201ca2ec0dc647f57fa20e6c76fa50eb903d166..1ee9933a4182e832897d6848765289d818404552 100644 --- a/framework/modules/c_InterestingFilesModule/README.txt +++ b/framework/modules/c_InterestingFilesModule/README.txt @@ -36,7 +36,7 @@ The configuration file is an XML document that defines interesting file sets in terms of search criteria. Here is a sample: <?xml version="1.0" encoding="utf-8"?> -<INTERESTING_FILES> +<INTERESTING_FILES ignoreKnown="0"> <INTERESTING_FILE_SET name="HTMLFilesType" description="Files with extension .htm*"> <EXTENSION typeFilter="file">.htm*</EXTENSION> </INTERESTING_FILE_SET> @@ -97,7 +97,20 @@ search named "SuspiciousFiles" will find files and directories that end in attributes. Matches with this filter must contain the specified string as a sub-string of the file or directory path. +Known files (e.g. files in the NSRL) can be ignored by providing the +'ignoreKnown' attribute either on the top level 'INTERESTING_FILES' element +or on one or more 'INTERESTING_FILE_SET' elements. +The following valid values for the 'ignoreKnown' attribute are based on the +TskImgDB::KNOWN_STATUS enumeration in TskImgDB.h. + 0 - All known files (both good and bad) + 1 - Known good files + 2 - Known bad (or notable) files + 3 - Unknown files + +The ability to ignore known files depends on the existence of a hash database +along with hash calculation and lookup modules. + RESULTS The result of the lookup is written to the blackboard as an artifact. diff --git a/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.cpp b/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.cpp index 1771d8a824e8bba3b2d5d53b523ef79493b00d99..471854e959c1f11ee551147ff950b91601bfce5b 100644 --- a/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.cpp +++ b/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.cpp @@ -40,7 +40,8 @@ namespace { const size_t SECTOR_SIZE = 512; - const size_t DEFAULT_SECTORS_PER_READ = 32; + const size_t DEFAULT_SECTORS_PER_READ = 32; + static const char zeroBuffer[DEFAULT_SECTORS_PER_READ * SECTOR_SIZE] = { 0 }; } TskCarvePrepSectorConcat::TskCarvePrepSectorConcat() @@ -161,6 +162,8 @@ void TskCarvePrepSectorConcat::createUnallocSectorsImgFiles(const std::string &o int unallocSectorsImgId = 0; std::ofstream outfile; uint64_t currentFileOffset = 0; // In bytes + Poco::File currentFile; + do { // Keep track of the starting offsets in the output file (in bytes) and in the image (in sectors) of the sector run or part of a sector run @@ -194,17 +197,7 @@ void TskCarvePrepSectorConcat::createUnallocSectorsImgFiles(const std::string &o startingImageOffset += (currentFileOffset - startingFileOffset) / 512; } - // Close the current output file. - if (unallocSectorsImgId) - { - outfile.close(); - } - - // Schedule the current output file for carving. Note that derived classes can change this behavior by overriding onUnallocSectorsImgFileCreated. - if (currentFileOffset > 0) - { - onUnallocSectorsImgFileCreated(unallocSectorsImgId); - } + closeAndSchedule(unallocSectorsImgId, currentFile, outfile); // Get the next output file number. if (imgDB.addUnallocImg(unallocSectorsImgId) == -1) @@ -219,6 +212,7 @@ void TskCarvePrepSectorConcat::createUnallocSectorsImgFiles(const std::string &o // Create an output file in the subdirectory. path << Poco::Path::separator() << outputFileName.c_str(); + currentFile = path.str(); outfile.open(path.str().c_str(), std::ios_base::out|std::ios_base::binary); if (outfile.fail()) { @@ -243,6 +237,16 @@ void TskCarvePrepSectorConcat::createUnallocSectorsImgFiles(const std::string &o break; } + // If we are at the start of a new output file and the chunk we've just read + // contains nothing but zeros there is no need to write it to the file. + // This allows us skip carving for potentially large areas that contain nothing. + if (currentFileOffset == 0 && sectorBuffer[0] == 0 && !memcmp(sectorBuffer, zeroBuffer, sectorsRead * SECTOR_SIZE)) + { + sectorRunOffset += sectorsRead; + startingImageOffset += sectorsRead; + continue; + } + // Write the chunk of sectors to the output file. outfile.write(sectorBuffer, sectorsRead * 512); if (outfile.bad()) @@ -271,17 +275,7 @@ void TskCarvePrepSectorConcat::createUnallocSectorsImgFiles(const std::string &o } while(sectorRuns.next() != -1); - // Close the final output file. - if (unallocSectorsImgId) - { - outfile.close(); - } - - // Schedule the final output file. - if (currentFileOffset > 0) - { - onUnallocSectorsImgFileCreated(unallocSectorsImgId); - } + closeAndSchedule(unallocSectorsImgId, currentFile, outfile); if (sectorBuffer != NULL) { @@ -334,3 +328,22 @@ void TskCarvePrepSectorConcat::mapFileToImage(int unallocSectorsImgId, std::ofst throw TskException(msg.str()); } } + +void TskCarvePrepSectorConcat::closeAndSchedule(const int unallocSectorsImgId, Poco::File& outFile, std::ofstream& outFileStream) const +{ + if (unallocSectorsImgId == 0) + return; + + outFileStream.close(); + + // If this is a zero length file we remove it and skip carving. + if (outFile.getSize() == 0) + { + outFile.remove(); + TskServices::Instance().getImgDB().setUnallocImgStatus(unallocSectorsImgId, TskImgDB::IMGDB_UNALLOC_IMG_STATUS_CARVED_NOT_NEEDED); + } + else + { + onUnallocSectorsImgFileCreated(unallocSectorsImgId); + } +} \ No newline at end of file diff --git a/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.h b/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.h index ff3a03350a9fdcd4409b540773c7a1160a33e65f..c7cf67c1a71b5c48b38df63088884f24874d1353 100644 --- a/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.h +++ b/framework/tsk/framework/extraction/TskCarvePrepSectorConcat.h @@ -19,6 +19,9 @@ // TSK Framework includes #include "CarvePrep.h" +// Poco includes +#include "Poco/File.h" + // C/C++ library includes #include <string> @@ -131,6 +134,15 @@ class TSK_FRAMEWORK_API TskCarvePrepSectorConcat : public CarvePrep * @return Throws TskException on error. */ void mapFileToImage(int unallocSectorsImgId, std::ofstream & outfile, uint64_t startingFileOffset, uint64_t endingFileOffset, int volumeID, uint64_t startingImageOffset) const; + + /** + * Close the unallocated sectors image file and schedule for carving + * if necessary. + * Carving will not be necessary if it is a zero length file, in which + * case the file will be deleted and the unalloc image status record + * will be set to carving not needed. + */ + void closeAndSchedule(const int unallocSectorsImgId, Poco::File& outFile, std::ofstream& outFileStream) const; }; #endif diff --git a/framework/tsk/framework/services/TskImgDBPostgreSQL.cpp b/framework/tsk/framework/services/TskImgDBPostgreSQL.cpp index ba2f217e74bf55a6c5d3268d78facef4aaff36d7..536ddef64a6a1a13f74531a0dd66ab293b92e5e6 100755 --- a/framework/tsk/framework/services/TskImgDBPostgreSQL.cpp +++ b/framework/tsk/framework/services/TskImgDBPostgreSQL.cpp @@ -157,11 +157,11 @@ int TskImgDBPostgreSQL::initialize() return 1; } - if (initializePreparedStatements()) - { - // Error message will have been logged by initializePreparedStatements() - return 1; - } + //if (initializePreparedStatements()) + //{ + // // Error message will have been logged by initializePreparedStatements() + // return 1; + //} addToolInfo("DbSchema", IMGDB_SCHEMA_VERSION); LOGINFO(L"ImgDB Created."); @@ -273,7 +273,7 @@ int TskImgDBPostgreSQL::open() std::stringstream dbConnectionString; dbConnectionString << "host='" << db_host_ip << "' port='" << db_port - << "' dbname='" << m_dbName << "' user='" << name << "'"; + << "' dbname='" << Poco::replace(m_dbName, "'", "\\'") << "' user='" << name << "'"; m_dbConnection = new pqxx::connection(dbConnectionString.str()); } @@ -653,24 +653,35 @@ int TskImgDBPostgreSQL::addFsFileInfo(int fileSystemID, const TSK_FS_FILE *fileS try { // We don't provide file_id to the prepared function because it uses DEFAULT for that. - stmt << "EXECUTE addFsFileInfoPlan (" - << IMGDB_FILES_TYPE_FS << ", " - << IMGDB_FILES_STATUS_READY_FOR_ANALYSIS << ", " - << m_dbConnection->quote(fileName) << ", " - << parFileId << ", " - << fileSystemFile->name->type << ", " - << meta_type << ", " - << fileSystemFile->name->flags << ", " - << meta_flags << ", " - << size << ", " - << crtime << ", " - << ctime << ", " - << atime << ", " - << mtime << ", " - << meta_mode << ", " - << gid << ", " - << uid - << ", E" << m_dbConnection->quote(fullpath) << ")"; + stmt << "INSERT INTO files (file_id, type_id, status, name, par_file_id, dir_type, meta_type, dir_flags, meta_flags, size, crtime, ctime, atime, mtime, mode, gid, uid, full_path) VALUES (" + << "DEFAULT, " << IMGDB_FILES_TYPE_FS << ", " << IMGDB_FILES_STATUS_READY_FOR_ANALYSIS << ", " << m_dbConnection->quote(fileName) << ", " + << parFileId << ", " << fileSystemFile->name->type << ", " << meta_type << ", " + << fileSystemFile->name->flags << ", " << meta_flags << ", " << size << ", " << crtime << ", " << ctime << ", " << atime << ", " + << mtime << ", " << meta_mode << ", " << gid << ", " << uid << ", E" << m_dbConnection->quote(fullpath) << ")" + << " RETURNING file_id"; + + // Commenting out to see if the addition of prepared statements is + // the cause of the frequent PostgreSQL server crashes we've seen + // recently. + + //stmt << "EXECUTE addFsFileInfoPlan (" + // << IMGDB_FILES_TYPE_FS << ", " + // << IMGDB_FILES_STATUS_READY_FOR_ANALYSIS << ", " + // << m_dbConnection->quote(fileName) << ", " + // << parFileId << ", " + // << fileSystemFile->name->type << ", " + // << meta_type << ", " + // << fileSystemFile->name->flags << ", " + // << meta_flags << ", " + // << size << ", " + // << crtime << ", " + // << ctime << ", " + // << atime << ", " + // << mtime << ", " + // << meta_mode << ", " + // << gid << ", " + // << uid + // << ", E" << m_dbConnection->quote(fullpath) << ")"; result R = executeStatement(stmt.str()); @@ -1367,16 +1378,28 @@ int TskImgDBPostgreSQL::addCarvedFileInfo(int vol_id, const char *name, uint64_t stringstream stmt; - stmt << "EXECUTE addCarvedFileInfoPlan (" - << IMGDB_FILES_TYPE_CARVED << ", " - << IMGDB_FILES_STATUS_CREATED << ", " - << m_dbConnection->quote(utf8Name) << ", " - << TSK_FS_NAME_TYPE_REG << ", " - << TSK_FS_META_TYPE_REG << ", " - << TSK_FS_NAME_FLAG_UNALLOC << ", " - << TSK_FS_META_FLAG_UNALLOC << ", " - << size << "," - << m_dbConnection->quote(utf8Name) << ")"; + // Commenting out to see if the addition of prepared statements is + // the cause of the frequent PostgreSQL server crashes we've seen + // recently. + + //stmt << "EXECUTE addCarvedFileInfoPlan (" + // << IMGDB_FILES_TYPE_CARVED << ", " + // << IMGDB_FILES_STATUS_CREATED << ", " + // << m_dbConnection->quote(utf8Name) << ", " + // << TSK_FS_NAME_TYPE_REG << ", " + // << TSK_FS_META_TYPE_REG << ", " + // << TSK_FS_NAME_FLAG_UNALLOC << ", " + // << TSK_FS_META_FLAG_UNALLOC << ", " + // << size << "," + // << m_dbConnection->quote(utf8Name) << ")"; + + stmt << "INSERT INTO files (file_id, type_id, name, par_file_id, dir_type, meta_type," + "dir_flags, meta_flags, size, ctime, crtime, atime, mtime, mode, uid, gid, status, full_path) " + "VALUES (DEFAULT, " << IMGDB_FILES_TYPE_CARVED << ", " << m_dbConnection->quote(utf8Name) + << ", NULL, " << TSK_FS_NAME_TYPE_REG << ", " << TSK_FS_META_TYPE_REG << ", " + << TSK_FS_NAME_FLAG_UNALLOC << ", " << TSK_FS_META_FLAG_UNALLOC << ", " + << size << ", 0, 0, 0, 0, NULL, NULL, NULL, " << IMGDB_FILES_STATUS_CREATED << "," << m_dbConnection->quote(utf8Name) << ")" + << " RETURNING file_id"; try { @@ -1467,19 +1490,28 @@ int TskImgDBPostgreSQL::addDerivedFileInfo(const std::string& name, const uint64 std::stringstream stmt; - stmt << "EXECUTE addDerivedFileInfoPlan (" - << IMGDB_FILES_TYPE_DERIVED << ", " - << IMGDB_FILES_STATUS_CREATED << ", " - << m_dbConnection->quote(&cleanName[0]) << ", " - << parentId << ", " - << dirType << ", " - << metaType << ", " - << size << ", " - << crtime << ", " - << ctime << ", " - << atime << ", " - << mtime << ", " - << m_dbConnection->quote(&cleanPath[0]) << ")"; + // Commenting out to see if the addition of prepared statements is + // the cause of the frequent PostgreSQL server crashes we've seen + // recently. + + //stmt << "EXECUTE addDerivedFileInfoPlan (" + // << IMGDB_FILES_TYPE_DERIVED << ", " + // << IMGDB_FILES_STATUS_CREATED << ", " + // << m_dbConnection->quote(&cleanName[0]) << ", " + // << parentId << ", " + // << dirType << ", " + // << metaType << ", " + // << size << ", " + // << crtime << ", " + // << ctime << ", " + // << atime << ", " + // << mtime << ", " + // << m_dbConnection->quote(&cleanPath[0]) << ")"; + + stmt << "INSERT INTO files (file_id, type_id, name, par_file_id, dir_type, meta_type, size, ctime, crtime, atime, mtime, status, full_path) " + "VALUES (DEFAULT, " << IMGDB_FILES_TYPE_DERIVED << ", " << m_dbConnection->quote(&cleanName[0]) << ", " << parentId << ", " << dirType << ", " << metaType << ", " << size + << ", " << ctime << ", " << crtime << ", " << atime << ", " << mtime << ", " << IMGDB_FILES_STATUS_CREATED << ", E" << m_dbConnection->quote(&cleanPath[0]) << ")" + << " RETURNING file_id"; try { diff --git a/m4/ac_prog_java.m4 b/m4/ac_prog_java.m4 new file mode 100644 index 0000000000000000000000000000000000000000..1ba1688702ae9dbe97edfe5cc40a9412d24df64b --- /dev/null +++ b/m4/ac_prog_java.m4 @@ -0,0 +1,83 @@ +dnl @synopsis AC_PROG_JAVA +dnl +dnl Here is a summary of the main macros: +dnl +dnl AC_PROG_JAVAC: finds a Java compiler. +dnl +dnl AC_PROG_JAVA: finds a Java virtual machine. +dnl +dnl AC_CHECK_CLASS: finds if we have the given class (beware of +dnl CLASSPATH!). +dnl +dnl AC_CHECK_RQRD_CLASS: finds if we have the given class and stops +dnl otherwise. +dnl +dnl AC_TRY_COMPILE_JAVA: attempt to compile user given source. +dnl +dnl AC_TRY_RUN_JAVA: attempt to compile and run user given source. +dnl +dnl AC_JAVA_OPTIONS: adds Java configure options. +dnl +dnl AC_PROG_JAVA tests an existing Java virtual machine. It uses the +dnl environment variable JAVA then tests in sequence various common +dnl Java virtual machines. For political reasons, it starts with the +dnl free ones. You *must* call [AC_PROG_JAVAC] before. +dnl +dnl If you want to force a specific VM: +dnl +dnl - at the configure.in level, set JAVA=yourvm before calling +dnl AC_PROG_JAVA +dnl +dnl (but after AC_INIT) +dnl +dnl - at the configure level, setenv JAVA +dnl +dnl You can use the JAVA variable in your Makefile.in, with @JAVA@. +dnl +dnl *Warning*: its success or failure can depend on a proper setting of +dnl the CLASSPATH env. variable. +dnl +dnl TODO: allow to exclude virtual machines (rationale: most Java +dnl programs cannot run with some VM like kaffe). +dnl +dnl Note: This is part of the set of autoconf M4 macros for Java +dnl programs. It is VERY IMPORTANT that you download the whole set, +dnl some macros depend on other. Unfortunately, the autoconf archive +dnl does not support the concept of set of macros, so I had to break it +dnl for submission. +dnl +dnl A Web page, with a link to the latest CVS snapshot is at +dnl <http://www.internatif.org/bortzmeyer/autoconf-Java/>. +dnl +dnl This is a sample configure.in Process this file with autoconf to +dnl produce a configure script. +dnl +dnl AC_INIT(UnTag.java) +dnl +dnl dnl Checks for programs. +dnl AC_CHECK_CLASSPATH +dnl AC_PROG_JAVAC +dnl AC_PROG_JAVA +dnl +dnl dnl Checks for classes +dnl AC_CHECK_RQRD_CLASS(org.xml.sax.Parser) +dnl AC_CHECK_RQRD_CLASS(com.jclark.xml.sax.Driver) +dnl +dnl AC_OUTPUT(Makefile) +dnl +dnl @category Java +dnl @author Stephane Bortzmeyer <bortzmeyer@pasteur.fr> +dnl @version 2000-07-19 +dnl @license GPLWithACException + +AC_DEFUN([AC_PROG_JAVA],[ +AC_REQUIRE([AC_EXEEXT])dnl +if test x$JAVAPREFIX = x; then + test x$JAVA = x && AC_CHECK_PROGS(JAVA, kaffe$EXEEXT java$EXEEXT) +else + test x$JAVA = x && AC_CHECK_PROGS(JAVA, kaffe$EXEEXT java$EXEEXT, $JAVAPREFIX) +fi +test x$JAVA = x && AC_MSG_WARN([no acceptable Java virtual machine found in \$PATH]) +AC_PROG_JAVA_WORKS +AC_PROVIDE([$0])dnl +]) diff --git a/m4/ac_prog_java_works.m4 b/m4/ac_prog_java_works.m4 new file mode 100644 index 0000000000000000000000000000000000000000..cc71eb2866f39219183436a4814ac7ab9bf11051 --- /dev/null +++ b/m4/ac_prog_java_works.m4 @@ -0,0 +1,101 @@ +dnl @synopsis AC_PROG_JAVA_WORKS +dnl +dnl Internal use ONLY. +dnl +dnl Note: This is part of the set of autoconf M4 macros for Java +dnl programs. It is VERY IMPORTANT that you download the whole set, +dnl some macros depend on other. Unfortunately, the autoconf archive +dnl does not support the concept of set of macros, so I had to break it +dnl for submission. The general documentation, as well as the sample +dnl configure.in, is included in the AC_PROG_JAVA macro. +dnl +dnl @category Java +dnl @author Stephane Bortzmeyer <bortzmeyer@pasteur.fr> +dnl @version 2000-07-19 +dnl @license GPLWithACException + +AC_DEFUN([AC_PROG_JAVA_WORKS], [ +AC_CHECK_PROG(uudecode, uudecode$EXEEXT, yes) +if test x$JAVA != x; then +if test x$uudecode = xyes; then +AC_CACHE_CHECK([if uudecode can decode base 64 file], ac_cv_prog_uudecode_base64, [ +dnl /** +dnl * Test.java: used to test if java compiler works. +dnl */ +dnl public class Test +dnl { +dnl +dnl public static void +dnl main( String[] argv ) +dnl { +dnl System.exit (0); +dnl } +dnl +dnl } +cat << \EOF > Test.uue +begin-base64 644 Test.class +yv66vgADAC0AFQcAAgEABFRlc3QHAAQBABBqYXZhL2xhbmcvT2JqZWN0AQAE +bWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAARDb2RlAQAPTGluZU51 +bWJlclRhYmxlDAAKAAsBAARleGl0AQAEKEkpVgoADQAJBwAOAQAQamF2YS9s +YW5nL1N5c3RlbQEABjxpbml0PgEAAygpVgwADwAQCgADABEBAApTb3VyY2VG +aWxlAQAJVGVzdC5qYXZhACEAAQADAAAAAAACAAkABQAGAAEABwAAACEAAQAB +AAAABQO4AAyxAAAAAQAIAAAACgACAAAACgAEAAsAAQAPABAAAQAHAAAAIQAB +AAEAAAAFKrcAErEAAAABAAgAAAAKAAIAAAAEAAQABAABABMAAAACABQ= +==== +EOF +if uudecode$EXEEXT Test.uue; then + ac_cv_prog_uudecode_base64=yes +else + echo "configure: __oline__: uudecode had trouble decoding base 64 file 'Test.uue'" >&AC_FD_CC + echo "configure: failed file was:" >&AC_FD_CC + cat Test.uue >&AC_FD_CC + ac_cv_prog_uudecode_base64=no +fi +rm -f Test.uue]) +fi +if test x$ac_cv_prog_uudecode_base64 != xyes; then + rm -f Test.class + AC_MSG_WARN([I have to compile Test.class from scratch]) + if test x$ac_cv_prog_javac_works = xno; then + AC_MSG_ERROR([Cannot compile java source. $JAVAC does not work properly]) + fi + if test x$ac_cv_prog_javac_works = x; then + AC_PROG_JAVAC + fi +fi +AC_CACHE_CHECK(if $JAVA works, ac_cv_prog_java_works, [ +JAVA_TEST=Test.java +CLASS_TEST=Test.class +TEST=Test +changequote(, )dnl +cat << \EOF > $JAVA_TEST +/* [#]line __oline__ "configure" */ +public class +Test { +public static void main (String args[]) { + System.exit(0); +} } +EOF +changequote([, ])dnl +if test x$ac_cv_prog_uudecode_base64 != xyes; then + if AC_TRY_COMMAND($JAVAC $JAVACFLAGS $JAVA_TEST) && test -s $CLASS_TEST; then + : + else + echo "configure: failed program was:" >&AC_FD_CC + cat $JAVA_TEST >&AC_FD_CC + AC_MSG_ERROR(The Java compiler $JAVAC failed (see config.log, check the CLASSPATH?)) + fi +fi + if AC_TRY_COMMAND($JAVA $JAVAFLAGS $TEST) >/dev/null 2>&1; then + ac_cv_prog_java_works=yes +else + echo "configure: failed program was:" >&AC_FD_CC + cat $JAVA_TEST >&AC_FD_CC + AC_MSG_ERROR(The Java VM $JAVA failed (see config.log, check the CLASSPATH?)) +fi +rm -fr $JAVA_TEST $CLASS_TEST Test.uue]) +AC_PROVIDE([$0])dnl +fi +] +) + diff --git a/man/img_cat.1 b/man/img_cat.1 index 06f348bc7e80959f314c9de64b553d286bbf93a9..e0fdf92e3e85fdfc53fae0cfef7705c6ae444bc1 100644 --- a/man/img_cat.1 +++ b/man/img_cat.1 @@ -2,7 +2,7 @@ .SH NAME img_cat \- Output contents of an image file. .SH SYNOPSIS -.B img_cat [-i imgtype] [-b dev_sector_size] [-b start_sector] [-e stop_sector] [-vV] +.B img_cat [-i imgtype] [-b dev_sector_size] [-s start_sector] [-e stop_sector] [-vV] .I image [images] .SH DESCRIPTION .B img_cat diff --git a/tools/fiwalk/src/fiwalk.cpp b/tools/fiwalk/src/fiwalk.cpp index 8bfba32f36c5a322d8dc74aa2c78b62557a23d9f..e32c55aa3cd33994ae9567e981cca16dc51295be 100644 --- a/tools/fiwalk/src/fiwalk.cpp +++ b/tools/fiwalk/src/fiwalk.cpp @@ -664,13 +664,6 @@ int main(int argc, char * const *argv1) fprintf(stderr,"ERROR: fiwalk was compiled without AFF support.\n"); exit(0); #else -#if 0 - if((tsk_img_type_supported() & TSK_IMG_TYPE_AFF_AFF)==0){ - fprintf(stderr,"ERROR: fiwalk was compiled with AFF support but the TSK library is not.\n"); - fprintf(stderr,"tsk_img_type_supported=0x%x\n",tsk_img_type_supported()); - exit(0); - } -#endif #endif } diff --git a/tsk/auto/auto_db.cpp b/tsk/auto/auto_db.cpp index 37190621f3e335155071d2f228bca0812bebee1a..e87d17891dd9128584b9db890d66bc02dd338b56 100644 --- a/tsk/auto/auto_db.cpp +++ b/tsk/auto/auto_db.cpp @@ -193,19 +193,20 @@ uint8_t uint8_t TskAutoDb::addImageDetails(const char *const img_ptrs[], int a_num) { - string md5 = ""; -#if HAVE_LIBEWF - if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { +// string md5 = ""; +//#if HAVE_LIBEWF +// if (m_img_info->itype == TSK_IMG_TYPE_EWF_EWF) { // @@@ This shoudl really probably be inside of a tsk_img_ method - IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info; - if (ewf_info->md5hash_isset) { - md5 = ewf_info->md5hash; - } - } -#endif +// IMG_EWF_INFO *ewf_info = (IMG_EWF_INFO *)m_img_info; +// if (ewf_info->md5hash_isset) { +// md5 = ewf_info->md5hash; +// } +// } +//#endif if (m_db->addImageInfo(m_img_info->itype, m_img_info->sector_size, - m_curImgId, m_curImgTZone, m_img_info->size, md5)) { + // m_curImgId, m_curImgTZone, m_img_info->size, md5)) { + m_curImgId, m_curImgTZone)) { return 1; } diff --git a/tsk/auto/db_sqlite.cpp b/tsk/auto/db_sqlite.cpp index 0fefa672b053051ba72416e0065e23004d7a9741..3a53d32f7ffb83cc21adde0e007976e05bf83a34 100755 --- a/tsk/auto/db_sqlite.cpp +++ b/tsk/auto/db_sqlite.cpp @@ -25,7 +25,6 @@ using std::stringstream; using std::sort; using std::for_each; - #define TSK_SCHEMA_VER 4 /** @@ -238,7 +237,9 @@ int "Error creating tsk_objects table: %s\n") || attempt_exec - ("CREATE TABLE tsk_image_info (obj_id INTEGER PRIMARY KEY, type INTEGER, ssize INTEGER, tzone TEXT, size INTEGER, md5 TEXT);", +// ("CREATE TABLE tsk_image_info (obj_id INTEGER PRIMARY KEY, type INTEGER, ssize INTEGER, tzone TEXT, size INTEGER, md5 TEXT);", + + ("CREATE TABLE tsk_image_info (obj_id INTEGER PRIMARY KEY, type INTEGER, ssize INTEGER, tzone TEXT);", "Error creating tsk_image_info table: %s\n") || attempt_exec @@ -433,9 +434,14 @@ int objId = sqlite3_last_insert_rowid(m_db); +// snprintf(stmt, 1024, +// "INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5) VALUES (%lld, %d, %d, '%s', %"PRIuOFF", '%s');", +// objId, type, ssize, timezone.c_str(), size, md5.c_str()); + snprintf(stmt, 1024, - "INSERT INTO tsk_image_info (obj_id, type, ssize, tzone, size, md5) VALUES (%lld, %d, %d, '%s', %"PRIuOFF", '%s');", - objId, type, ssize, timezone.c_str(), size, md5.c_str()); + "INSERT INTO tsk_image_info (obj_id, type, ssize, tzone) VALUES (%lld, %d, %d, '%s');", + objId, type, ssize, timezone.c_str()); + return attempt_exec(stmt, "Error adding data to tsk_image_info table: %s\n"); } diff --git a/tsk/fs/tsk_yaffs.h b/tsk/fs/tsk_yaffs.h index bd29dd220a968632cb0a30cc824a57423baa9582..93ccc1bceb0386eae2fbb7eaab4035c7916a1e42 100644 --- a/tsk/fs/tsk_yaffs.h +++ b/tsk/fs/tsk_yaffs.h @@ -34,6 +34,30 @@ extern "C" { #define YAFFS_DEFAULT_MAX_TEST_BLOCKS 400 // Maximum number of blocks to test looking for Yaffs2 spare under auto-detect +#define YAFFS_HELP_MESSAGE "See http://wiki.sleuthkit.org/index.php?title=YAFFS2 for help on Yaffs2 configuration" + +/* + * Yaffs config file constants and return values + */ +#ifdef TSK_WIN32 +#define YAFFS_CONFIG_FILE_SUFFIX L"-yaffs2.config" +#else +#define YAFFS_CONFIG_FILE_SUFFIX "-yaffs2.config" +#endif + +#define YAFFS_CONFIG_SEQ_NUM_STR "spare_seq_num_offset" +#define YAFFS_CONFIG_OBJ_ID_STR "spare_obj_id_offset" +#define YAFFS_CONFIG_CHUNK_ID_STR "spare_chunk_id_offset" +#define YAFFS_CONFIG_PAGE_SIZE_STR "flash_page_size" +#define YAFFS_CONFIG_SPARE_SIZE_STR "flash_spare_size" +#define YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR "flash_chunks_per_block" + +typedef enum { + YAFFS_CONFIG_OK, + YAFFS_CONFIG_FILE_NOT_FOUND, + YAFFS_CONFIG_ERROR +} YAFFS_CONFIG_STATUS; + /* ** Yaffs Object Flags */ diff --git a/tsk/fs/yaffs.cpp b/tsk/fs/yaffs.cpp index fdb61921f916b2dba26fce6990ea365bf5ddbfcd..0984cabee43e74913ce46eda69469afdedc3da74 100644 --- a/tsk/fs/yaffs.cpp +++ b/tsk/fs/yaffs.cpp @@ -28,6 +28,10 @@ v** Copyright (c) 2002-2003 Brian Carrier, @stake Inc. All rights reserved --*/ #include <vector> +#include <map> +#include <algorithm> +#include <string> +#include <set> #include "tsk_fs_i.h" #include "tsk_yaffs.h" @@ -611,34 +615,44 @@ static void static void yaffscache_objects_free(YAFFSFS_INFO *yfs) { - YaffsCacheObject *obj = yfs->cache_objects; - while(obj != NULL) { - YaffsCacheObject *to_free = obj; + if((yfs != NULL) && (yfs->cache_objects != NULL)){ + YaffsCacheObject *obj = yfs->cache_objects; + while(obj != NULL) { + YaffsCacheObject *to_free = obj; + + YaffsCacheVersion *ver = obj->yco_latest; + while(ver != NULL) { + YaffsCacheVersion *v_to_free = ver; + ver = ver->ycv_prior; + free(v_to_free); + } - YaffsCacheVersion *ver = obj->yco_latest; - while(ver != NULL) { - YaffsCacheVersion *v_to_free = ver; - ver = ver->ycv_prior; - free(v_to_free); + obj = obj->yco_next; + free(to_free); } - - obj = obj->yco_next; - free(to_free); } } static void yaffscache_chunks_free(YAFFSFS_INFO *yfs) { - std::map<unsigned int,YaffsCacheChunkGroup>::iterator iter; - for( iter = yfs->chunkMap->begin(); iter != yfs->chunkMap->end(); ++iter ) { - YaffsCacheChunk *chunk = yfs->chunkMap->operator[](iter->first).cache_chunks_head; - while(chunk != NULL) { - YaffsCacheChunk *to_free = chunk; - chunk = chunk->ycc_next; - free(to_free); + if((yfs != NULL) && (yfs->chunkMap != NULL)){ + // Free the YaffsCacheChunks in each ChunkGroup + std::map<unsigned int,YaffsCacheChunkGroup>::iterator iter; + for( iter = yfs->chunkMap->begin(); iter != yfs->chunkMap->end(); ++iter ) { + YaffsCacheChunk *chunk = yfs->chunkMap->operator[](iter->first).cache_chunks_head; + while(chunk != NULL) { + YaffsCacheChunk *to_free = chunk; + chunk = chunk->ycc_next; + free(to_free); + } } + + // Free the map + yfs->chunkMap->clear(); + delete yfs->chunkMap; } + } @@ -649,6 +663,212 @@ static void * */ +/* Function to parse config file + * + * @param img_info Image info for this image + * @param map<string, int> Stores values from config file indexed on parameter name + * @returns YAFFS_CONFIG_STATUS One of YAFFS_CONFIG_OK, YAFFS_CONFIG_FILE_NOT_FOUND, or YAFFS_CONFIG_ERROR + */ +static YAFFS_CONFIG_STATUS +yaffs_load_config_file(TSK_IMG_INFO * a_img_info, std::map<std::string, std::string> & results){ + const TSK_TCHAR ** image_names; + int num_imgs; + size_t config_file_name_len; + TSK_TCHAR * config_file_name; + FILE* config_file; + char buf[1001]; + + // Get the image name(s) + image_names = tsk_img_get_names(a_img_info, &num_imgs); + if(num_imgs < 1){ + return YAFFS_CONFIG_ERROR; + } + + // Construct the name of the config file from the first image name + config_file_name_len = TSTRLEN(image_names[0]); + config_file_name_len += TSTRLEN(YAFFS_CONFIG_FILE_SUFFIX); + config_file_name = (TSK_TCHAR *) tsk_malloc(sizeof(TSK_TCHAR) * (config_file_name_len + 1)); + + TSTRNCPY(config_file_name, image_names[0], TSTRLEN(image_names[0]) + 1); + TSTRNCAT(config_file_name, YAFFS_CONFIG_FILE_SUFFIX, TSTRLEN(YAFFS_CONFIG_FILE_SUFFIX) + 1); + +#ifdef TSK_WIN32 + HANDLE hWin; + + if ((hWin = CreateFile(config_file_name, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, 0, + 0)) == INVALID_HANDLE_VALUE) { + + // For the moment, assume that the file just doesn't exist, which isn't an error + free(config_file_name); + return YAFFS_CONFIG_FILE_NOT_FOUND; + } + config_file = _fdopen(_open_osfhandle((intptr_t) hWin, _O_RDONLY), "r"); + if (config_file == NULL) { + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_load_config: Error converting Windows handle to C handle"); + free(config_file_name); + CloseHandle(hWin); + return YAFFS_CONFIG_ERROR; + } +#else + if (NULL == (config_file = fopen(config_file_name, "r"))) { + free(config_file_name); + return YAFFS_CONFIG_FILE_NOT_FOUND; + } +#endif + + while(fgets(buf, 1000, config_file) != NULL){ + + // Is it a comment? + if((buf[0] == '#') || (buf[0] == ';')){ + continue; + } + + // Is there a '=' ? + if(strchr(buf, '=') == NULL){ + continue; + } + + // Copy to strings while removing whitespace and converting to lower case + std::string paramName(""); + std::string paramVal(""); + + const char * paramNamePtr = strtok(buf, "="); + while(*paramNamePtr != '\0'){ + if(! isspace((char)(*paramNamePtr))){ + paramName += tolower((char)(*paramNamePtr)); + } + paramNamePtr++; + } + + const char * paramValPtr = strtok(NULL, "="); + while(*paramValPtr != '\0'){ + if(! isspace(*paramValPtr)){ + paramVal += tolower((char)(*paramValPtr)); + } + paramValPtr++; + } + + // Make sure this parameter is not already in the map + if(results.find(paramName) != results.end()){ + // Duplicate parameter - return an error + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_load_config: Duplicate parameter name in config file (\"%s\"). %s", paramName.c_str(), YAFFS_HELP_MESSAGE); + fclose(config_file); + free(config_file_name); + return YAFFS_CONFIG_ERROR; + } + + // Add this entry to the map + results[paramName] = paramVal; + } + + fclose(config_file); + free(config_file_name); + return YAFFS_CONFIG_OK; +} + +/* + * Helper function for yaffs_validate_config + * Tests that a string consists only of digits and has at least one digit + * (Can modify later if we want negative fields to be valid) + * + * @param numStr String to test + * @returns 1 on error, 0 on success + */ +static int +yaffs_validate_integer_field(std::string numStr){ + unsigned int i; + + // Test if empty + if(numStr.length() == 0){ + return 1; + } + + // Test each character + for(i = 0;i < numStr.length();i++){ + if(! isdigit(numStr[i])){ + return 1; + } + } + + return 0; +} + +/* + * Function to validate the contents of the config file + * Currently testing: + * All YAFFS_CONFIG fields should be integers (if they exist) + * Either need all three of YAFFS_CONFIG_SEQ_NUM_STR, YAFFS_CONFIG_OBJ_ID_STR, YAFFS_CONFIG_CHUNK_ID_STR + * or none of them + * + * @param paramMap Holds mapping of parameter name to parameter value + * @returns 1 on error (invalid parameters), 0 on success + */ +static int +yaffs_validate_config_file(std::map<std::string, std::string> & paramMap){ + int offset_field_count; + + // Make a list of all fields to test + std::set<std::string> integerParams; + integerParams.insert(YAFFS_CONFIG_SEQ_NUM_STR); + integerParams.insert(YAFFS_CONFIG_OBJ_ID_STR); + integerParams.insert(YAFFS_CONFIG_CHUNK_ID_STR); + integerParams.insert(YAFFS_CONFIG_PAGE_SIZE_STR); + integerParams.insert(YAFFS_CONFIG_SPARE_SIZE_STR); + integerParams.insert(YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR); + + // If the parameter is set, verify that the value is an int + for(std::set<std::string>::iterator it = integerParams.begin();it != integerParams.end();it++){ + if((paramMap.find(*it) != paramMap.end()) && + (0 != yaffs_validate_integer_field(paramMap[*it]))){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_validate_config_file: Empty or non-integer value for Yaffs2 parameter \"%s\". %s", (*it).c_str(), YAFFS_HELP_MESSAGE); + return 1; + } + } + + // Check that we have all three spare offset fields, or none of the three + offset_field_count = 0; + if(paramMap.find(YAFFS_CONFIG_SEQ_NUM_STR) != paramMap.end()){ + offset_field_count++; + } + if(paramMap.find(YAFFS_CONFIG_OBJ_ID_STR) != paramMap.end()){ + offset_field_count++; + } + if(paramMap.find(YAFFS_CONFIG_CHUNK_ID_STR) != paramMap.end()){ + offset_field_count++; + } + + if(! ((offset_field_count == 0) || (offset_field_count == 3))){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_validate_config_file: Require either all three spare offset fields or none. %s", YAFFS_HELP_MESSAGE); + return 1; + } + + // Make sure there aren't any unexpected fields present + for(std::map<std::string, std::string>::iterator it = paramMap.begin(); it != paramMap.end();it++){ + if(integerParams.find(it->first) == integerParams.end()){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr( + "yaffs_validate_config_file: Found unexpected field in config file (\"%s\"). %s", it->first.c_str(), YAFFS_HELP_MESSAGE); + return 1; + } + } + + return 0; +} + /* * Function to attempt to determine the layout of the yaffs spare area. * Results of the analysis (if the format could be determined) will be stored @@ -666,6 +886,8 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ unsigned int chunksToTest = 10; // Number of chunks to test in each block unsigned int minChunksRead = 10; // Minimum number of chunks we require to run the test (we might not get the full number we want to test for a very small file) + unsigned int chunkSize = yfs->page_size + yfs->spare_size; + unsigned int blockSize = yfs->chunks_per_block * chunkSize; TSK_FS_INFO *fs = &(yfs->fs_info); unsigned int cnt; @@ -677,6 +899,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ unsigned int currentOffset; unsigned char * allSpares; + unsigned int allSparesLength; TSK_OFF_T offset; TSK_OFF_T maxBlocks; @@ -696,19 +919,23 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ int thisChunkBase; int lastChunkBase; - if ((spareBuffer = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) { + // The spare area needs to be at least 16 bytes to run the test + if(yfs->spare_size < 16){ + if(tsk_verbose && (! yfs->autoDetect)){ + tsk_fprintf(stderr, + "yaffs_initialize_spare_format failed - given spare size (%d) is not large enough to contain needed fields\n", yfs->spare_size); + } return TSK_ERR; } - if ((allSpares = (unsigned char*) tsk_malloc(yfs->spare_size * blocksToTest * chunksToTest)) == NULL) { - free(spareBuffer); + if ((spareBuffer = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) { return TSK_ERR; } - // Initialize the array containing the spares so that uninitialized entries won't cause failure if we don't have enough - // data to fill it. - for(i = 0;i < yfs->spare_size * blocksToTest * chunksToTest;i++){ - allSpares[i] = 0x01; + allSparesLength = yfs->spare_size * blocksToTest * chunksToTest; + if ((allSpares = (unsigned char*) tsk_malloc(allSparesLength)) == NULL) { + free(spareBuffer); + return TSK_ERR; } // Initialize the pointers to one of the configurations we've seen (thought these defaults should not get used) @@ -727,7 +954,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // observed exception. // Calculate the number of blocks in the image - maxBlocks = yfs->fs_info.img_info->size / (yfs->chunks_per_block * (yfs->page_size + yfs->spare_size)); + maxBlocks = yfs->fs_info.img_info->size / (yfs->chunks_per_block * chunkSize); // If maxBlocksToTest = 0 (unlimited), set it to the total number of blocks // Also reduce the number of blocks to test if it is larger than the total number of blocks @@ -740,7 +967,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ for(TSK_OFF_T blockIndex = 0;blockIndex < maxBlocksToTest;blockIndex++){ // Read the last spare area that we want to test first - offset = (TSK_OFF_T)blockIndex * yfs->chunks_per_block * (yfs->page_size + yfs->spare_size) + (chunksToTest - 1) * (yfs->page_size + yfs->spare_size) + yfs->page_size; + offset = (TSK_OFF_T)blockIndex * blockSize + (chunksToTest - 1) * chunkSize + yfs->page_size; cnt = tsk_img_read(fs->img_info, offset, (char *) spareBuffer, yfs->spare_size); if (cnt == -1 || cnt < yfs->spare_size) { @@ -774,7 +1001,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // Copy all earlier spare areas in the block for(chunkIndex = 0;chunkIndex < chunksToTest - 1;chunkIndex++){ - offset = blockIndex * yfs->chunks_per_block * (yfs->page_size + yfs->spare_size) + chunkIndex * (yfs->page_size + yfs->spare_size) + yfs->page_size; + offset = blockIndex * blockSize + chunkIndex * chunkSize + yfs->page_size; cnt = tsk_img_read(fs->img_info, offset, (char *) spareBuffer, yfs->spare_size); if (cnt == -1 || cnt < yfs->spare_size) { @@ -817,7 +1044,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // Print out the collected spare areas if we're in verbose mode if(tsk_verbose && (! yfs->autoDetect)){ - for(blockIndex = 0;blockIndex < blocksToTest;blockIndex++){ + for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++){ for(chunkIndex = 0;chunkIndex < chunksToTest;chunkIndex++){ for(i = 0;i < yfs->spare_size;i++){ fprintf(stderr, "%02x", allSpares[blockIndex * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + i]); @@ -828,9 +1055,9 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ } // Test all indices into the spare area (that leave enough space for all 16 bytes) - for(currentOffset = 0;currentOffset < yfs->spare_size - 16;currentOffset++){ + for(currentOffset = 0;currentOffset <= yfs->spare_size - 16;currentOffset++){ goodOffset = 1; - for(blockIndex = 0;blockIndex < blocksToTest;blockIndex++){ + for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++){ for(chunkIndex = 1;chunkIndex < chunksToTest;chunkIndex++){ lastChunkBase = blockIndex * yfs->spare_size * chunksToTest + (chunkIndex - 1) * yfs->spare_size; @@ -901,7 +1128,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ break; } } - if(allSameByte && (allSpares[thisChunkBase + currentOffset] != 0x01)){ // allSpares was initialized with all 0x01 - there might be lines of it left + if(allSameByte){ if(tsk_verbose && (! yfs->autoDetect)){ tsk_fprintf(stderr, "yaffs_initialize_spare_format: Elimimating offset %d - all repeated bytes\n", @@ -937,7 +1164,7 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ // We probably don't want the first byte to always be 0xff int firstByteFF = 1; - for(blockIndex = 0;blockIndex < blocksToTest;blockIndex++){ + for(blockIndex = 0;blockIndex < nBlocksTested;blockIndex++){ for(chunkIndex = 1;chunkIndex < chunksToTest;chunkIndex++){ if(allSpares[blockIndex * yfs->spare_size * chunksToTest + chunkIndex * yfs->spare_size + currentOffset] != 0xff){ firstByteFF = 0; @@ -993,6 +1220,8 @@ yaffs_initialize_spare_format(YAFFSFS_INFO * yfs, TSK_OFF_T maxBlocksToTest){ tsk_fprintf(stderr, "yaffs_initialize_spare_format: Final offsets: %d (sequence number), %d (object id), %d (chunk id), %d (n bytes)\n", bestOffset, bestOffset+4, bestOffset+8, bestOffset+12); + tsk_fprintf(stderr, + "If these do not seem valid: %s\n", YAFFS_HELP_MESSAGE); } return TSK_OK; } @@ -1079,11 +1308,18 @@ static uint8_t uint32_t object_id; uint32_t chunk_id; + // Should have checked this by now, but just in case + if((yfs->spare_seq_offset + 4 > yfs->spare_size) || + (yfs->spare_obj_id_offset + 4 > yfs->spare_size) || + (yfs->spare_chunk_id_offset + 4 > yfs->spare_size)){ + return 1; + } + if ((spr = (unsigned char*) tsk_malloc(yfs->spare_size)) == NULL) { return 1; } - if (yfs->spare_size < 46) { + if (yfs->spare_size < 46) { // Why is this 46? tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("yaffsfs_read_spare: spare size is too small"); @@ -1528,7 +1764,6 @@ static uint8_t uint8_t type; char *real_name; - if (a_fs_file == NULL) { tsk_error_set_errno(TSK_ERR_FS_ARG); tsk_error_set_errstr("yaffsfs_inode_lookup: fs_file is NULL"); @@ -1569,6 +1804,10 @@ static uint8_t return 1; } + if(version->ycv_header_chunk == NULL){ + return 1; + } + if (yaffsfs_read_chunk(yfs, &header, &spare, version->ycv_header_chunk->ycc_offset) != TSK_OK) { if (tsk_verbose) tsk_fprintf(stderr, "yaffs_inode_lookup: yaffsfs_read_chunk failed!\n"); @@ -2218,17 +2457,18 @@ static uint8_t static void yaffsfs_close(TSK_FS_INFO *fs) { - YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs; + if(fs != NULL){ + YAFFSFS_INFO *yfs = (YAFFSFS_INFO *)fs; - fs->tag = 0; + fs->tag = 0; - // TODO: Walk and free the cache structures - yaffscache_objects_free(yfs); - yaffscache_chunks_free(yfs); + // Walk and free the cache structures + yaffscache_objects_free(yfs); + yaffscache_chunks_free(yfs); - //tsk_deinit_lock(&yaffsfs->lock); - - tsk_fs_free(fs); + //tsk_deinit_lock(&yaffsfs->lock); + tsk_fs_free(fs); + } } typedef struct _dir_open_cb_args { @@ -2384,6 +2624,7 @@ static TSK_RETVAL_ENUM return TSK_ERR; } + if ((fs_dir->fs_file = tsk_fs_file_open_meta(a_fs, NULL, a_addr)) == NULL) { tsk_error_errstr2_concat(" - yaffs_dir_open_meta"); @@ -2661,12 +2902,14 @@ TSK_FS_INFO * yaffs2_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, TSK_FS_TYPE_ENUM ftype, uint8_t test) { - YAFFSFS_INFO *yaffsfs; - TSK_FS_INFO *fs; + YAFFSFS_INFO *yaffsfs = NULL; + TSK_FS_INFO *fs = NULL; const unsigned int psize = img_info->page_size; const unsigned int ssize = img_info->spare_size; YaffsHeader * first_header = NULL; TSK_FS_DIR *test_dir; + std::map<std::string, std::string> configParams; + YAFFS_CONFIG_STATUS config_file_status; // clean up any error messages that are lying around tsk_error_reset(); @@ -2680,13 +2923,47 @@ TSK_FS_INFO * if ((yaffsfs = (YAFFSFS_INFO *) tsk_fs_malloc(sizeof(YAFFSFS_INFO))) == NULL) return NULL; + yaffsfs->cache_objects = NULL; + yaffsfs->chunkMap = NULL; + + // Read config file (if it exists) + config_file_status = yaffs_load_config_file(img_info, configParams); + if(config_file_status == YAFFS_CONFIG_ERROR){ + // tsk_error was set by yaffs_load_config + goto on_error; + } + else if(config_file_status == YAFFS_CONFIG_OK){ + // Validate the input + // If it fails validation, return (tsk_error will be set up already) + if(1 == yaffs_validate_config_file(configParams)){ + goto on_error; + } + } + + // If we read these fields from the config file, use those values. Otherwise use the defaults + if(configParams.find(YAFFS_CONFIG_PAGE_SIZE_STR) != configParams.end()){ + yaffsfs->page_size = atoi(configParams[YAFFS_CONFIG_PAGE_SIZE_STR].c_str()); + } + else{ + yaffsfs->page_size = psize == 0 ? YAFFS_DEFAULT_PAGE_SIZE : psize; + } + + if(configParams.find(YAFFS_CONFIG_SPARE_SIZE_STR) != configParams.end()){ + yaffsfs->spare_size = atoi(configParams[YAFFS_CONFIG_SPARE_SIZE_STR].c_str()); + } + else{ + yaffsfs->spare_size = ssize == 0 ? YAFFS_DEFAULT_SPARE_SIZE : ssize; + } + + if(configParams.find(YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR) != configParams.end()){ + yaffsfs->chunks_per_block = atoi(configParams[YAFFS_CONFIG_CHUNKS_PER_BLOCK_STR].c_str()); + } + else{ + yaffsfs->chunks_per_block = 64; + } - yaffsfs->page_size = psize == 0 ? YAFFS_DEFAULT_PAGE_SIZE : psize; - yaffsfs->spare_size = ssize == 0 ? YAFFS_DEFAULT_SPARE_SIZE : ssize; - yaffsfs->chunks_per_block = 64; // TODO: Why are 2 different memory allocation methods used in the same code? // This makes things unnecessary complex. - yaffsfs->chunkMap = new std::map<uint32_t, YaffsCacheChunkGroup>; yaffsfs->max_obj_id = 1; yaffsfs->max_version = 0; @@ -2708,23 +2985,45 @@ TSK_FS_INFO * fs->endian = TSK_LIT_ENDIAN; // Determine the layout of the spare area + // If it was specified in the config file, use those values. Otherwise do the auto-detection + if(configParams.find(YAFFS_CONFIG_SEQ_NUM_STR) != configParams.end()){ + // In the validation step, we ensured that if one of the offsets was set, we have all of them + yaffsfs->spare_seq_offset = atoi(configParams[YAFFS_CONFIG_SEQ_NUM_STR].c_str()); + yaffsfs->spare_obj_id_offset = atoi(configParams[YAFFS_CONFIG_OBJ_ID_STR].c_str()); + yaffsfs->spare_chunk_id_offset = atoi(configParams[YAFFS_CONFIG_CHUNK_ID_STR].c_str()); + + // Check that the offsets are valid for the given spare area size (fields are 4 bytes long) + if((yaffsfs->spare_seq_offset + 4 > yaffsfs->spare_size) || + (yaffsfs->spare_obj_id_offset + 4 > yaffsfs->spare_size) || + (yaffsfs->spare_chunk_id_offset + 4 > yaffsfs->spare_size)){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS); + tsk_error_set_errstr("yaffs2_open: Offset(s) in config file too large for spare area (size %d). %s", yaffsfs->spare_size, YAFFS_HELP_MESSAGE); + goto on_error; + } - // Decide how many blocks to test. If we're not doing auto-detection, set to zero (no limit) - unsigned int maxBlocksToTest; - if(yaffsfs->autoDetect){ - maxBlocksToTest = YAFFS_DEFAULT_MAX_TEST_BLOCKS; + + // nBytes isn't currently used, so just set to zero + yaffsfs->spare_nbytes_offset = 0; } else{ - maxBlocksToTest = 0; - } + // Decide how many blocks to test. If we're not doing auto-detection, set to zero (no limit) + unsigned int maxBlocksToTest; + if(yaffsfs->autoDetect){ + maxBlocksToTest = YAFFS_DEFAULT_MAX_TEST_BLOCKS; + } + else{ + maxBlocksToTest = 0; + } - if(yaffs_initialize_spare_format(yaffsfs, maxBlocksToTest) != TSK_OK){ - tsk_error_reset(); - tsk_error_set_errno(TSK_ERR_FS_MAGIC); - tsk_error_set_errstr("not a YAFFS file system (bad spare format)"); - if (tsk_verbose) - fprintf(stderr, "yaffsfs_open: could not find valid spare area format\n"); - goto on_error; + if(yaffs_initialize_spare_format(yaffsfs, maxBlocksToTest) != TSK_OK){ + tsk_error_reset(); + tsk_error_set_errno(TSK_ERR_FS_MAGIC); + tsk_error_set_errstr("not a YAFFS file system (bad spare format). %s", YAFFS_HELP_MESSAGE); + if (tsk_verbose) + fprintf(stderr, "yaffsfs_open: could not find valid spare area format\n%s\n", YAFFS_HELP_MESSAGE); + goto on_error; + } } /* @@ -2736,9 +3035,9 @@ TSK_FS_INFO * if (yaffsfs_read_header(yaffsfs, &first_header, 0)) { tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); - tsk_error_set_errstr("not a YAFFS file system (first record)"); + tsk_error_set_errstr("not a YAFFS file system (first record). %s", YAFFS_HELP_MESSAGE); if (tsk_verbose) - fprintf(stderr, "yaffsfs_open: invalid first record\n"); + fprintf(stderr, "yaffsfs_open: invalid first record\n%s\n", YAFFS_HELP_MESSAGE); goto on_error; } free(first_header); @@ -2798,44 +3097,34 @@ TSK_FS_INFO * * cache is shared among threads. */ //tsk_init_lock(&yaffsfs->lock); - yaffsfs->cache_objects = NULL; + yaffsfs->chunkMap = new std::map<uint32_t, YaffsCacheChunkGroup>; yaffsfs_cache_fs(yaffsfs); if (tsk_verbose) { fprintf(stderr, "yaffsfs_open: done building cache!\n"); //yaffscache_objects_dump(yaffsfs, stderr); } - fflush(stderr); // Update the number of inums now that we've read in the file system fs->inum_count = fs->last_inum - 1; test_dir = tsk_fs_dir_open_meta(fs, fs->root_inum); if (test_dir == NULL) { - yaffsfs_close(fs); - tsk_error_reset(); tsk_error_set_errno(TSK_ERR_FS_MAGIC); - tsk_error_set_errstr("not a YAFFS file system (no root directory)"); + tsk_error_set_errstr("not a YAFFS file system (no root directory). %s", YAFFS_HELP_MESSAGE); if (tsk_verbose) - fprintf(stderr, "yaffsfs_open: invalid file system\n"); - return NULL; + fprintf(stderr, "yaffsfs_open: invalid file system\n%s\n", YAFFS_HELP_MESSAGE); + goto on_error; } tsk_fs_dir_close(test_dir); return fs; on_error: - // Make sure to free yaffsfs here otherwise it will leak - if( yaffsfs != NULL ) { - // TODO: where is chunkMap freed in normal operations? - if( yaffsfs->chunkMap != NULL ) { - yaffsfs->chunkMap->clear(); + // yaffsfs_close frees all the cache objects + yaffsfs_close(fs); - delete yaffsfs->chunkMap; - } - free( yaffsfs ); - } return NULL; }