[Forensics-changes] [SCM] Tool to help recover deleted files on ext3 filesystems branch, upstream, updated. upstream/0.9.0-1-gdb6697a

Daniel Baumann daniel at debian.org
Thu Nov 6 15:41:58 UTC 2008


The following commit has been merged in the upstream branch:
commit db6697afb8dd0b8f258b55b9c6be11110cc04f07
Author: Daniel Baumann <daniel at debian.org>
Date:   Thu Nov 6 16:38:26 2008 +0100

    Adding upstream version 0.10.0.

diff --git a/Makefile.in b/Makefile.in
index 19f1241..c852228 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -85,6 +85,8 @@ CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CWD_FLAGS = @CWD_FLAGS@
 CWD_LIBS = @CWD_LIBS@
+CWD_R_FLAGS = @CWD_R_FLAGS@
+CWD_R_LIBS = @CWD_R_LIBS@
 CW_DEBUG_FLAGS = @CW_DEBUG_FLAGS@
 CW_OPTIMISE_FLAGS = @CW_OPTIMISE_FLAGS@
 CW_STRIPPED_CXXFLAGS = @CW_STRIPPED_CXXFLAGS@
diff --git a/aclocal.m4 b/aclocal.m4
index aaf5a16..9dd73eb 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -927,7 +927,7 @@ AC_DEFUN([CW_AUTOMACROS], [dnl
 dnl Check cwautomacros version.
 minver=$1
 test -n "$minver" || minver=0
-if test 20080607 -lt $minver; then
+if test 20081012 -lt $minver; then
   AC_MSG_ERROR([cwautomacros version $minver or later is required.])
 fi
 dnl Detect unexpanded macros.
@@ -1020,8 +1020,12 @@ touch $1/timestamp-$2
 # AC_ARG_ENABLE(debugging, [  --enable-debugging      enable debugging code.])
 # Where OPTIONNAME is [debugging] and WANTED is [$enable_debugging].
 #
-# THREADED can be [yes] or [no] when the application is threaded
-# or non-threaded respectively.
+# THREADED can be [yes], [no] or [both] when the application is
+# threaded, non-threaded or when both are needed respectively.
+# If THREADED is set to [both] then CWD_FLAGS and CWD_LIBS
+# are set as appropriate for the non-threaded case and
+# CWD_R_FLAGS and CWD_R_LIBS are set as appropriate for
+# the threaded case.
 #
 # This macro tests for the usability of libcwd and sets the macro
 # `cw_used_libcwd' to "yes" when it is detected, "no" otherwise.
@@ -1049,11 +1053,22 @@ else
   AC_LINK_IFELSE([AC_LANG_CALL([], [__libcwd_version])], [cw_cv_lib_libcwd=yes], [cw_cv_lib_libcwd=no])
   LIBS="$cw_save_LIBS"
   AC_LANG_RESTORE])
+  if test "$3" = "both"; then
+    AC_CACHE_CHECK([if libcwd_r is available], cw_cv_lib_libcwd_r,
+[    # Check if we have libcwd_r
+    AC_LANG_SAVE
+    AC_LANG_CPLUSPLUS
+    cw_save_LIBS="$LIBS"
+    LIBS="$LIBS `pkg-config --libs libcwd_r`"
+    AC_LINK_IFELSE([AC_LANG_CALL([], [__libcwd_version])], [cw_cv_lib_libcwd_r=yes], [cw_cv_lib_libcwd_r=no])
+    LIBS="$cw_save_LIBS"
+    AC_LANG_RESTORE])
+  fi
   cw_use_libcwd="$cw_wanted"
   test -n "$cw_use_libcwd" || cw_use_libcwd=auto
   test "$cw_use_libcwd" = "auto" && cw_use_libcwd=$cw_cv_lib_libcwd
   if test "$cw_use_libcwd" = "yes"; then
-    if test "$cw_cv_lib_libcwd" = "no"; then
+    if test "$cw_cv_lib_libcwd" = "no" -o "$3" = "both" -a x"$cw_cv_lib_libcwd_r" = x"no"; then
       m4_default([$5], [dnl
       AC_MSG_ERROR([
   --enable-$1: You need to have libcwd installed to enable this.
@@ -1061,9 +1076,19 @@ else
   PKG_CONFIG_PATH=/opt/install/lib/pkgconfig LD_LIBRARY_PATH=/opt/install/lib ./configure])])
     else
       cw_used_libcwd=yes
-      m4_default([$4], [dnl
-      CWD_FLAGS="`pkg-config --cflags lib$cw_libname`"
-      CWD_LIBS="`pkg-config --libs lib$cw_libname`"])
+      if test "$3" = "both"; then
+	m4_default([$4], [dnl
+	CWD_FLAGS="`pkg-config --cflags libcwd`"
+	CWD_LIBS="`pkg-config --libs libcwd`"
+	CWD_R_FLAGS="`pkg-config --cflags libcwd_r`"
+	CWD_R_LIBS="`pkg-config --libs libcwd_r`"])
+	AC_SUBST(CWD_R_FLAGS)
+	AC_SUBST(CWD_R_LIBS)
+      else
+	m4_default([$4], [dnl
+	CWD_FLAGS="`pkg-config --cflags lib$cw_libname`"
+	CWD_LIBS="`pkg-config --libs lib$cw_libname`"])
+      fi
       AC_SUBST(CWD_FLAGS)
       AC_SUBST(CWD_LIBS)
     fi
@@ -1234,7 +1259,7 @@ esac
 # Handle cw_config_libcwd.
 # Check if we have libcwd, $cw_config_libcwd can be "yes", "no" or "".
 if test -z "$cw_used_libcwd"; then
-CW_LIB_LIBCWD([libcwd], [$cw_config_libcwd], [no])
+CW_LIB_LIBCWD([libcwd], [$cw_config_libcwd], [both])
 fi
 USE_LIBCWD="$cw_used_libcwd"
 AC_SUBST([USE_LIBCWD])
diff --git a/configure b/configure
index 975719d..7d8bc3f 100755
--- a/configure
+++ b/configure
@@ -1,7 +1,7 @@
 #! /bin/sh
 # From configure.ac Revision: 7 .
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for ext3grep 0.9.0.
+# Generated by GNU Autoconf 2.61 for ext3grep 0.10.0.
 #
 # Report bugs to <carlo at alinoe.com>.
 #
@@ -575,8 +575,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='ext3grep'
 PACKAGE_TARNAME='ext3grep'
-PACKAGE_VERSION='0.9.0'
-PACKAGE_STRING='ext3grep 0.9.0'
+PACKAGE_VERSION='0.10.0'
+PACKAGE_STRING='ext3grep 0.10.0'
 PACKAGE_BUGREPORT='carlo at alinoe.com'
 
 ac_unique_file="src/ext3grep.cc"
@@ -713,6 +713,8 @@ host
 host_cpu
 host_vendor
 host_os
+CWD_R_FLAGS
+CWD_R_LIBS
 CWD_FLAGS
 CWD_LIBS
 USE_LIBCWD
@@ -1254,7 +1256,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ext3grep 0.9.0 to adapt to many kinds of systems.
+\`configure' configures ext3grep 0.10.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1324,7 +1326,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ext3grep 0.9.0:";;
+     short | recursive ) echo "Configuration of ext3grep 0.10.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1420,7 +1422,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ext3grep configure 0.9.0
+ext3grep configure 0.10.0
 generated by GNU Autoconf 2.61
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1434,7 +1436,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by ext3grep $as_me 0.9.0, which was
+It was created by ext3grep $as_me 0.10.0, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   $ $0 $@
@@ -2131,7 +2133,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='ext3grep'
- VERSION='0.9.0'
+ VERSION='0.10.0'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -2303,7 +2305,7 @@ fi
 
 minver=20080318
 test -n "$minver" || minver=0
-if test 20080607 -lt $minver; then
+if test 20081012 -lt $minver; then
   { { echo "$as_me:$LINENO: error: cwautomacros version $minver or later is required." >&5
 echo "$as_me: error: cwautomacros version $minver or later is required." >&2;}
    { (exit 1); exit 1; }; }
@@ -5156,7 +5158,7 @@ if test x"$cw_wanted" = x"no"; then
   cw_used_libcwd=no
 else
   cw_libname=cwd
-  test "no" = "yes" && cw_libname=cwd_r
+  test "both" = "yes" && cw_libname=cwd_r
   { echo "$as_me:$LINENO: checking if libcwd is available" >&5
 echo $ECHO_N "checking if libcwd is available... $ECHO_C" >&6; }
 if test "${cw_cv_lib_libcwd+set}" = set; then
@@ -5232,11 +5234,88 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 fi
 { echo "$as_me:$LINENO: result: $cw_cv_lib_libcwd" >&5
 echo "${ECHO_T}$cw_cv_lib_libcwd" >&6; }
+  if test "both" = "both"; then
+    { echo "$as_me:$LINENO: checking if libcwd_r is available" >&5
+echo $ECHO_N "checking if libcwd_r is available... $ECHO_C" >&6; }
+if test "${cw_cv_lib_libcwd_r+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Check if we have libcwd_r
+
+    ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+    cw_save_LIBS="$LIBS"
+    LIBS="$LIBS `pkg-config --libs libcwd_r`"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char __libcwd_version ();
+int
+main ()
+{
+return __libcwd_version ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  cw_cv_lib_libcwd_r=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	cw_cv_lib_libcwd_r=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+    LIBS="$cw_save_LIBS"
+    ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ echo "$as_me:$LINENO: result: $cw_cv_lib_libcwd_r" >&5
+echo "${ECHO_T}$cw_cv_lib_libcwd_r" >&6; }
+  fi
   cw_use_libcwd="$cw_wanted"
   test -n "$cw_use_libcwd" || cw_use_libcwd=auto
   test "$cw_use_libcwd" = "auto" && cw_use_libcwd=$cw_cv_lib_libcwd
   if test "$cw_use_libcwd" = "yes"; then
-    if test "$cw_cv_lib_libcwd" = "no"; then
+    if test "$cw_cv_lib_libcwd" = "no" -o "both" = "both" -a x"$cw_cv_lib_libcwd_r" = x"no"; then
             { { echo "$as_me:$LINENO: error:
   --enable-libcwd: You need to have libcwd installed to enable this.
   Or perhaps you need to add its location to PKG_CONFIG_PATH and LD_LIBRARY_PATH, for example:
@@ -5248,8 +5327,17 @@ echo "$as_me: error:
    { (exit 1); exit 1; }; }
     else
       cw_used_libcwd=yes
-            CWD_FLAGS="`pkg-config --cflags lib$cw_libname`"
-      CWD_LIBS="`pkg-config --libs lib$cw_libname`"
+      if test "both" = "both"; then
+		CWD_FLAGS="`pkg-config --cflags libcwd`"
+	CWD_LIBS="`pkg-config --libs libcwd`"
+	CWD_R_FLAGS="`pkg-config --cflags libcwd_r`"
+	CWD_R_LIBS="`pkg-config --libs libcwd_r`"
+
+
+      else
+		CWD_FLAGS="`pkg-config --cflags lib$cw_libname`"
+	CWD_LIBS="`pkg-config --libs lib$cw_libname`"
+      fi
 
 
     fi
@@ -6551,7 +6639,7 @@ exec 6>&1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ext3grep $as_me 0.9.0, which was
+This file was extended by ext3grep $as_me 0.10.0, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6604,7 +6692,7 @@ Report bugs to <bug-autoconf at gnu.org>."
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF
 ac_cs_version="\\
-ext3grep config.status 0.9.0
+ext3grep config.status 0.10.0
 configured by $0, generated by GNU Autoconf 2.61,
   with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
@@ -6923,6 +7011,8 @@ _ACEOF
 ac_delim='%!_!# '
 for ac_last_try in false false false false false :; do
   cat >conf$$subs.sed <<_ACEOF
+CWD_R_FLAGS!$CWD_R_FLAGS$ac_delim
+CWD_R_LIBS!$CWD_R_LIBS$ac_delim
 CWD_FLAGS!$CWD_FLAGS$ac_delim
 CWD_LIBS!$CWD_LIBS$ac_delim
 USE_LIBCWD!$USE_LIBCWD$ac_delim
@@ -6950,7 +7040,7 @@ LIBOBJS!$LIBOBJS$ac_delim
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF
 
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 25; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 27; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
diff --git a/configure.ac b/configure.ac
index 73b1035..b18c728 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@ dnl CW_VERSION_MINOR               : Increment when major changes have occured.
 dnl CW_VERSION_REVISION            : Increment every public release; or set to 0 when CW_VERSION_MINOR was incremented.
 
 define(CW_VERSION_MAJOR, 0)
-define(CW_VERSION_MINOR, 9)
+define(CW_VERSION_MINOR, 10)
 define(CW_VERSION_REVISION, 0)
 
 define(CW_PACKAGE_NAME, [ext3grep])
diff --git a/src/Makefile.in b/src/Makefile.in
index c9aef55..c6a7576 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -148,6 +148,8 @@ CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CWD_FLAGS = @CWD_FLAGS@
 CWD_LIBS = @CWD_LIBS@
+CWD_R_FLAGS = @CWD_R_FLAGS@
+CWD_R_LIBS = @CWD_R_LIBS@
 CW_DEBUG_FLAGS = @CW_DEBUG_FLAGS@
 CW_OPTIMISE_FLAGS = @CW_OPTIMISE_FLAGS@
 CW_STRIPPED_CXXFLAGS = @CW_STRIPPED_CXXFLAGS@
diff --git a/src/custom.cc b/src/custom.cc
index 287ee0c..1c69ecf 100644
--- a/src/custom.cc
+++ b/src/custom.cc
@@ -43,10 +43,59 @@
 #include "init_consts.h"
 #include "print_inode_to.h"
 
-// This file was written and used for custom job: recovering emails
-// on a 40 GB partition that had no information left in the journal
-// and had been mounted for a week since the deletion.
+// The first part of this file was written and used for custom job:
+// recovering emails on a 40 GB partition that had no information
+// left in the journal and had been mounted for a week since the deletion.
 // In the end, 85% of the emails were recovered.
+//
+// The second part of this file was written and used for another
+// custom job: recovering a vmware flat file of 322 GB.
+// In the end, 100% was recovered.
+
+bool is_double_indirect_block(unsigned char* block_ptr)
+{
+  static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
+  std::cout << "Calling is_indirect_block(*)..." << std::endl;
+  if (!is_indirect_block(block_ptr), true)
+    return false;
+  uint32_t* p = reinterpret_cast<uint32_t*>(block_ptr);
+  do
+  {
+    unsigned char* indirect_block_ptr = get_block(*p, block_buf);
+    std::cout << "Calling is_indirect_block(" << *p << ")..." << std::endl;
+    std::cout << "Group: " << block_to_group(super_block, *p) << "; block: " << *p << std::endl;
+    if (!is_indirect_block(indirect_block_ptr), true)
+      return false;
+  }
+  while (*++p);
+  return true;
+}
+
+bool is_tripple_indirect_block(unsigned char* block_ptr)
+{
+  static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
+  std::cout << "Calling is_indirect_block(*)..." << std::endl;
+  if (!is_indirect_block(block_ptr), true)
+    return false;
+  uint32_t* p = reinterpret_cast<uint32_t*>(block_ptr);
+  bool res = true;
+  do
+  {
+    unsigned char* indirect_block_ptr = get_block(*p, block_buf);
+    std::cout << "Calling is_double_indirect_block(" << *p << ")..." << std::endl;
+    std::cout << "Group: " << block_to_group(super_block, *p) << "; block: " << *p << std::endl;
+    if (!is_double_indirect_block(indirect_block_ptr))
+    {
+      res = false;
+      std::cout << "FAILED! But continue anyway..." << std::endl;
+      //return false;
+    }
+  }
+  while (*++p);
+  return res;
+}
+
+#if 0
 
 // This must be set to the full email address (I obfuscated it before
 // committing this to SVN for obvious reasons).
@@ -288,8 +337,6 @@ block_size_pairs_st block_size_pairs[block_size_pairs_size] = {
   { 6141983, 2494271 }
 };
 
-#if 1
-
 struct Data {
   bool one_block;		// Set if the block ends on zeroes.
   bool sent;			// Set if the headers have a line containing "SquirrelMail authenticated user EMAILADDRESS".
@@ -378,38 +425,6 @@ int get_block_size(unsigned char* block_ptr)
   return size;
 }
 
-bool is_double_indirect_block(unsigned char* block_ptr)
-{
-  static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
-  if (!is_indirect_block(block_ptr))
-    return false;
-  uint32_t* p = reinterpret_cast<uint32_t*>(block_ptr);
-  do
-  {
-    unsigned char* indirect_block_ptr = get_block(*p, block_buf);
-    if (!is_indirect_block(indirect_block_ptr))
-      return false;
-  }
-  while (*++p);
-  return true;
-}
-
-bool is_tripple_indirect_block(unsigned char* block_ptr)
-{
-  static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
-  if (!is_indirect_block(block_ptr))
-    return false;
-  uint32_t* p = reinterpret_cast<uint32_t*>(block_ptr);
-  do
-  {
-    unsigned char* indirect_block_ptr = get_block(*p, block_buf);
-    if (!is_double_indirect_block(indirect_block_ptr))
-      return false;
-  }
-  while (*++p);
-  return true;
-}
-
 enum answer_t {
   contiguous,
   not_contiguous,
@@ -1183,7 +1198,7 @@ void custom(void)
         continue;
       InodePointer inode(get_inode(inode_number));
       // Skip inodes with size 0 (whatever).
-      if (inode->size() == 0)
+      if (inode->size() == 0)	// Files with size 0 exist, of course.
         continue;
       // Skip symlinks.
       if (is_symlink(inode))
@@ -1207,3 +1222,697 @@ void custom(void)
 }
 #endif
 
+//-----------------------------------------------------------------------------
+//
+// The code of the second custom job starts here.
+//
+// This code was written and used to successfully recover a 322 GB vmware file,
+// containing an ext3 filesystem with all emails and websites of all clients
+// of a small webhosting company. Work hours: 80. Recovery: 100%.
+
+// This table contained blocks changed by running fsck.
+int fsckd_blocks[] = {
+  0 // Deleted to save space in this demonstration code.
+};
+
+union block_t {
+  struct data_st {
+    int file_block_offset;
+    int sequence_number;
+  } data;
+  unsigned char raw[4096];
+};
+static block_t buf;
+
+void custom_action(int block_nr, int file_block_nr, void*)
+{
+  static int last_file_block_nr = -1;
+  if (file_block_nr != -1)
+  {
+    if (file_block_nr != ++last_file_block_nr)
+    {
+      std::cout << "SKIPPED " << (file_block_nr - last_file_block_nr) << " BLOCKS, file blocks " << last_file_block_nr << " up till and including " << (file_block_nr - 1) << "!" << std::endl;
+      last_file_block_nr = file_block_nr;
+    }
+    get_block(block_nr, buf.raw);
+    std::cout << "buf.data.file_block_offset = " << buf.data.file_block_offset << "; file_block_nr = " << file_block_nr << std::endl;
+    assert(buf.data.file_block_offset == file_block_nr);
+  }
+  assert(block_nr);
+  if (block_nr == 167575554 || (block_nr >= 167606272 && block_nr <= 167606300))
+  {
+    std::cout << "USING BLOCK " << block_nr << "OVERWRITTEN BY FOREMOST!" << std::endl;
+    //assert(false);
+  }
+  for (unsigned int i = 0; i < sizeof(fsckd_blocks) / sizeof(fsckd_blocks[0]); ++i)
+  {
+    if (fsckd_blocks[i] == block_nr)
+    {
+      std::cout << "USING FSCK-ED BLOCK "<< block_nr << "!" << std::endl;
+      //assert(false);
+    }
+  }
+  std::cout << "File block nr: " << file_block_nr << "; block: " << block_nr << std::endl;
+}
+
+struct block_color {
+  int blocknr;
+  block_color(int bn) : blocknr(bn) { }
+  friend std::ostream& operator<<(std::ostream& os, block_color const& bc)
+  {
+    int group = block_to_group(super_block, bc.blocknr);
+    int first_block = group_to_block(super_block, group);
+    int last_block = first_block + 32767;
+    if ((group & 1) == 1)
+      os << "\e[31m";
+    else
+      os << "\e[34m";
+    os << bc.blocknr << "\e[0m";
+    if (bc.blocknr == first_block)
+      os << '^';
+    else if (bc.blocknr == last_block)
+      os << '$';
+    return os;
+  }
+};
+
+class Range {
+  private:
+    bool first;
+    int last_blocknr;
+    int current_begin;
+    std::vector<std::pair<int, int> > v;
+
+  public:
+    Range(void) : first(true) { }
+
+    Range& operator+=(int blocknr)
+    {
+      if (first)
+      {
+	first = false;
+	current_begin = blocknr;
+      }
+      else
+      {
+	assert(blocknr > last_blocknr);
+        if (blocknr != last_blocknr + 1)
+	{
+	  v.push_back(std::pair<int, int>(current_begin, last_blocknr));
+	  current_begin = blocknr;
+	}
+      }
+      last_blocknr = blocknr;
+      return *this;
+    }
+
+    friend std::ostream& operator<<(std::ostream& os, Range const& range)
+    {
+      for (std::vector<std::pair<int, int> >::const_iterator iter = range.v.begin(); iter != range.v.end(); ++iter)
+	if (iter->first != iter->second)
+	  os << '[' << block_color(iter->first) << " - " << block_color(iter->second) <<
+	      " (" << (iter->second - iter->first + 1) << ")]";
+        else
+	  os << '[' << block_color(iter->first) << ']';
+      if (!range.first)
+      {
+	if (range.current_begin != range.last_blocknr)
+	  os << '[' << block_color(range.current_begin) << " - " << block_color(range.last_blocknr) <<
+	      " (" << (range.last_blocknr - range.current_begin + 1) << ")]";
+        else
+	  os << '[' << block_color(range.current_begin) << ']';
+      }
+      return os;
+    }
+};
+
+bool has_at_least_n_increasing_block_numbers(int n, unsigned char* block_buf)
+{
+  __le32* p = reinterpret_cast<__le32*>(block_buf);
+  __le32 last_block = 0;
+  int total = 0;
+  for (int i = 0; i < 1024; ++i)
+  {
+    if (p[i])
+    {
+      if (p[i] < last_block)
+	return false;
+      if (p[i] == last_block + 1)
+	++total;
+      last_block = p[i];
+    }
+  }
+  return total >= n;
+}
+
+// This function guesses what is the first valid Indirect Block in group 'group'.
+// Later I found a smarter way to do this by looking at the block bitmap updates
+// in the journal-- but this worked too.
+int first_indirect_block(int group, size_t& size)
+{
+  std::cout << "Entering first_indirect_block(" << group << ")\n";
+  static unsigned char block_buf[4096];
+  int first_block = group_to_block(super_block, group);
+  std::cout << "first_block = " << first_block << '\n';
+  int group_end = first_block + blocks_per_group(super_block);
+  std::cout << "group_end = " << group_end << '\n';
+  int freq[1025];
+  std::memset(freq, 0, sizeof(freq));
+  for (int b = first_block; b < group_end; ++b)
+  {
+    get_block(b, block_buf);
+    if (is_indirect_block(block_buf) && has_at_least_n_increasing_block_numbers(32, block_buf))
+    {
+      //std::cout << "Found indirect block at " << b << '\n';
+      freq[(b - first_block - 514) % 1025] += 1;	// 514 = bitmaps + inode table. 1025 = indirect block + its 1024 data blocks.
+    }
+  }
+  int best = 0;
+  int fbest = 0;
+  int total = 0;
+  std::vector<std::pair<int, int> > candidates;
+  for (int i = 0; i < 1025; ++i)
+  {
+    if (freq[i])
+    {
+      if (freq[i] > 1)
+      {
+	std::cout << i << " (" << freq[i] << "), ";
+	candidates.push_back(std::pair<int, int>(i, freq[i]));
+      }
+      total += freq[i];
+      if (freq[i] > fbest)
+      {
+	fbest = freq[i];
+	best = i;
+      }
+    }
+  }
+  std::cout << '\n';
+  std::cout << '\t' << "total: " << total << std::endl;
+  assert(!candidates.empty());
+  size = candidates.size();
+  if (size > 2)
+  {
+    std::cout << "FAILURE FOR GROUP " << group << ": indirect blocks are scattered! Returning -1.\n";
+    return -1;
+  }
+  if (size == 2)
+  {
+    int jump = 1;
+    if (group == 5146)
+      jump = 2;	// Special case, because this group contains the double indirect block.
+    int diff = candidates[1].first - candidates[0].first;
+    int sum = fbest;
+    if (diff == jump)
+    {
+      std::cout << "Choosing smallest of two\n";
+      best = candidates[0].first;
+      sum = candidates[0].second + candidates[1].second;
+    }
+    if (sum < 30)
+    {
+      std::cout << "WARNING: LOW INDIRECT BLOCK COUNT! There is a not insignificant chance that the heuristics fail in this case!\n";
+    }
+  }
+  else if (best > 0 && freq[best - 1] == 1)
+  {
+    size_t prev_size;
+    int prev_first_indirect_block = first_indirect_block(group - 1, prev_size);
+    // Turn it into the real block number.
+    prev_first_indirect_block += group_to_block(super_block, group - 1) + 514;
+    // Do the same for 'best - 1'.
+    int next_first_indirect_block = group_to_block(super_block, group) + 514 + best - 1;
+    // Use this heuristic magical formula to test if it's likely that in fact
+    // we have a pair with sizes (1, 30).
+    if ((prev_first_indirect_block + 520) % 1025 == next_first_indirect_block % 1025 && prev_size == 1)
+    {
+      std::cout << "Decrementing best!\n";
+      --best;
+    }
+  }
+  std::cout << "Leaving first_indirect_block() (returning " << best << ")\n";
+  return best;
+}
+
+int first_indirect_block(int group)
+{
+  size_t dummy;
+  return first_indirect_block(group, dummy);
+}
+
+int fib_table1[] = {
+  190, 742, 269, 821, 348, 900, 427, 979, 506, 33, 585, 112, 664, 191, 743, 270,
+  // ... lots of numbers deleted to save space in the demonstration code.
+  456, 1008, 535, 62
+};
+
+// WARNING: This table was HAND EDITTED (after generation).
+int fib_table2[] = {
+  570, 91, 644, 170, 722, 248, 800, 321, 873, 400, 952, 479, 6, 558, 85, 637,
+  // ... lots of numbers deleted to save space in the demonstration code.
+  198, 750, 277, 829, 356, 908, 435, 987, 514, 41, 593, 120, 672
+};
+
+// Manually recovered Tripple Indirect Block.
+int tib_table[] = {
+  168649904, 169716665, 170782906, 171849661, 172915902, 173982663, 175049424, 176115659,
+  177182420, 178248661, 179315416, 180381657, 181448418, 182514659, 117359, 1189230,
+  2256495, 3324280, 4392059, 5458300, 6525061, 7591302, 8659087, 9725322,
+  30015342, 31082103, 32151318, 33217553, 34284314, 35350549, 36417310, 37483551,
+  38550306, 39617067, 40683308, 41750069, 42816304, 43883065, 44949306, 46016067,
+  47082302, 48149063, 49215304, 50282065, 51348820, 52415061, 53481822, 54548057,
+  55614818, 56681059, 57747820, 58814061, 59880816, 60947057, 62013818, 63080573,
+  64146814, 65213575, 66279816, 67346571, 68412812, 69479573, 70545808, 71612569,
+  72680349, 73746590, 74813351, 75879586, 76946347, 78012588, 79080373, 80146614,
+  81213375, 82279616
+};
+
+__le32 const dib_169716665[/*1024*/] = {
+  /* 1024 block numbers deleted to save space */
+};
+
+__le32 const dib_39617067[/*1024*/] = {
+  /* 1024 block numbers deleted to save space */
+};
+
+__le32 const dib_49215304[/*1024*/] = {
+  /* 1024 block numbers deleted to save space */
+};
+
+__le32 const dib_56681059[/*1024*/] = {
+  /* 1024 block numbers deleted to save space */
+};
+
+__le32 const dib_66279816[/*1024*/] = {
+  /* 1024 block numbers deleted to save space */
+};
+
+// The function 'first_indirect_block' is rather slow.
+// Therefore it was once used to generate fib_table1 and fib_table2,
+// and this function uses the table.
+int first_indirect_block_table(int group)
+{
+  if (5147 <= group && group <= 5598)
+    return fib_table1[group - 5147];
+  else if (2 <= group && group <= 2510)
+  {
+    int res = fib_table2[group - 2];
+    if (res == -1)
+    {
+      std::cout << "FAILURE for group " << group << "\n\t";
+      first_indirect_block(group);
+      std::cout << std::endl;
+    }
+    assert(res != -1);
+    return res;
+  }
+
+  int res = first_indirect_block(group);
+  std::cout << "MISSING: first_indirect_block_table(" << group << ") = " << res << std::endl;
+  assert(res != -1);
+  return res;
+}
+
+// Given the block number of an (single) 'Indirect Block', return the next Indirect block
+// using the following heuristics: The next indirect block immediately follows the
+// last data block, which all immediately followed the last Indirect block; hence:
+// the next Indirect block is the current one plus 1025, UNLESS we reach the end
+// of a group. Then the next indirect block is retrieved from a table, which was
+// generated by determining what was the statistically most likely offset of such
+// equally spaced indirect blocks in the next group.
+__le32 next_indirect_block(__le32 current_indirect_block, bool need_restoring)
+{
+  // Special cases that the heuristics fail for:
+  if (current_indirect_block == 183467615)
+    return 183469160;
+  if (current_indirect_block == 183499910)
+    return 36375;
+  if (current_indirect_block == 10517828)
+    return 10519373;
+  if (current_indirect_block == 10523473)
+    return 10537013;
+  if (current_indirect_block == 10537013)
+    return 29726800;
+  if (current_indirect_block == 29754995)
+    return 29778002;
+  if (current_indirect_block == 32048904)
+    return 32052383;
+  int current_group = block_to_group(super_block, current_indirect_block);
+  int next_group = block_to_group(super_block, current_indirect_block + 1025);
+  if (current_group != next_group)
+  {
+    int group_start = group_to_block(super_block, current_group);
+    int group_end = group_start + blocks_per_group(super_block);
+    int left = group_end - current_indirect_block - 1;
+    std::cout << "left = " << left << std::endl;
+
+    int next_group_start = group_to_block(super_block, next_group);
+    std::cout << "next_group_start = " << next_group_start << std::endl;
+    int fib = need_restoring ? first_indirect_block(next_group) : first_indirect_block_table(next_group);
+    std::cout << "Returning " << next_group_start + fib + 514 << std::endl;
+    return next_group_start + fib + 514;
+  }
+  return current_indirect_block + 1025;
+}
+
+// Return true if this block is wiped (contains only zeroes).
+bool all_zeroes(__le32* indirect_block_buf)
+{
+  for(int i = 0; i < 1024; ++i)
+  {
+    if (indirect_block_buf[i])
+      return false;
+  }
+  return true;
+}
+
+// Retriece a Double Indirect Block.
+void get_dib(int block_number, __le32* buf)
+{
+  // These blocks were wiped during deletion.
+  if (block_number == 169716665)
+    memcpy(buf, dib_169716665, sizeof(dib_169716665));
+  else if (block_number == 39617067)
+    memcpy(buf, dib_39617067, sizeof(dib_39617067));
+  else if (block_number == 49215304)
+    memcpy(buf, dib_49215304, sizeof(dib_49215304));
+  else if (block_number == 56681059)
+    memcpy(buf, dib_56681059, sizeof(dib_56681059));
+  else if (block_number == 66279816)
+    memcpy(buf, dib_66279816, sizeof(dib_66279816));
+  else
+  // The rest is still intact.
+    get_block(block_number, (unsigned char*)buf);
+}
+
+void generate_sib(int sib_number, __le32* buf, __le32 next_indirect_block)
+{
+  int sib_group = block_to_group(super_block, sib_number);
+  int next_group_start = group_to_block(super_block, sib_group + 1);
+  if (sib_group == 5599)		// Last group?
+    next_group_start = 183500446;	// Byte-past-the-end in this group (fake a new group start).
+  int block_number = sib_number;
+  for (int k = 0; k < 1024; ++k)
+  {
+    ++block_number;
+    if (block_number == next_group_start)
+    {
+      int next_block_number = next_indirect_block - (1024 - k);
+#if 0	// Not a gamble anymore. See skip6check.
+      if (sib_number == 183499910)	// The only special case (manualy checked to be ok too)
+      {
+      std::cout << "Gambling that data block " << next_block_number << " follows " << (block_number - 1) << std::endl;
+      int group = block_to_group(super_block, next_block_number);
+      int next_group_start = group_to_block(super_block, group);
+      std::cout << "  number of skipped blocks at the start of group " << group << ": " << (next_block_number - next_group_start - 514) << std::endl;
+      }
+#endif
+      block_number = next_block_number;
+    }
+    buf[k] = block_number;
+  }
+}
+
+// This function retrieves a Single (normal) Indirect Block 'block_number').
+void get_sib(int block_number, __le32* buf, __le32 next_indirect_block)
+{
+  // These blocks were wiped during deletion.
+  if (block_number == 169868396 || block_number == 181632444 || block_number == 183499910 || block_number == 1900225 ||
+      block_number == 3801011 || block_number == 5668471 || block_number == 34111044 || block_number == 36011825 ||
+      block_number == 37879292 || block_number == 39780073 || block_number == 41680859 || block_number == 43548320 ||
+      block_number == 45449107 || block_number == 47316568 || block_number == 49249650 || block_number == 51117110 ||
+      block_number == 53017897 || block_number == 54918678 || block_number == 56819465 || block_number == 58720251 ||
+      block_number == 60620007 || block_number == 62520788 || block_number == 64421575 || block_number == 66386952 ||
+      block_number == 80608995 || block_number == 82509782)
+    generate_sib(block_number, buf, next_indirect_block);
+  else
+  // The rest is still intact.
+    get_block(block_number, (unsigned char*)buf);
+}
+
+#define DO_ACTUAL_RECOVERY 0
+
+#if DO_ACTUAL_RECOVERY
+int outfd;
+#endif
+int count = 0;
+
+struct timeval& operator-=(struct timeval& t1, struct timeval const& t2)
+{
+  t1.tv_sec -= t2.tv_sec;
+  if ((t1.tv_usec -= t2.tv_usec) < 0)
+  {
+    t1.tv_usec += 1000000;
+    --t1.tv_sec;
+  }
+  return t1;
+}
+
+static char const zeroes[4096] = { 0, };
+struct timeval start_time;
+struct timeval current_time;
+int const total_blocks = 78643200;
+int remaining_blocks = total_blocks;
+
+// This function is called for every data block.
+void process_data_block(int block_number)
+{
+#if DO_ACTUAL_RECOVERY
+  assert(block_number);
+  // If the block is a block that was corrupted by running 'foremost' after deletion,
+  // and before umounting, then gamble it originally only contained zeroes because
+  // many blocks before and after this region turn out to contain zeroes.
+  if (block_number == 167575554 || (block_number >= 167606272 && block_number <= 167606300))
+  {
+    int len = ::write(outfd, zeroes, 4096);
+    assert(len == 4096);
+  }
+  else
+  {
+    static unsigned char block_buf[4096];
+    get_block(block_number, block_buf);
+    int len = ::write(outfd, (char*)block_buf, 4096);
+    assert(len == 4096);
+  }
+#else
+  if (block_number == 167575554 || (block_number >= 167606272 && block_number <= 167606300))
+  {
+    std::cout << "Using foremost block " << block_number << " at file block " << count << '\n';
+  }
+#endif
+  ++count;
+  --remaining_blocks;
+  if (remaining_blocks % 10000 == 0)
+  {
+    gettimeofday(&current_time, NULL);
+    current_time -= start_time;
+    double seconds = current_time.tv_sec + current_time.tv_usec * 1e-6;
+    double blocks_per_second = (total_blocks - remaining_blocks) / seconds;
+    std::cout << "Speed: " << (blocks_per_second * 4.096e-3) << " MB/s. ";
+    double remaining_minutes = remaining_blocks / blocks_per_second / 60;
+    std::cout << "ETA: " << std::fixed << remaining_minutes << " minutes." << std::endl;
+  }
+}
+
+void custom(void)
+{
+#if 0
+  // This code was used to do many/various quick queries.
+  std::cout << block_to_group(super_block, 182514659) << '\n';
+  //std::cout << group_to_block(super_block, block_to_group(super_block, 2589192)) << '\n';
+  //first_indirect_block(5184);
+  return;
+#endif
+  //init_journal();
+#if 1
+  // This code was used to do the final recovery.
+#if DO_ACTUAL_RECOVERY
+  outfd = ::open("/home/carlo/RECOVERED.MOSES-DRIVE-322GB.VDMK-flat.vmdk", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0644);
+#endif
+  gettimeofday(&start_time, NULL);
+  int blocknrs[] = { 163021314, 163021315, 163021316, 163021317, 163021318, 163021319, 163054082, 163054083, 163054084, 163054085, 163054086, 163054087 };
+  for (int i = 0; i < (int)(sizeof(blocknrs) / sizeof(int)); ++i)
+  {
+    process_data_block(blocknrs[i]);
+  }
+  static __le32 sib_buf[1024];
+  get_block(167582637, (unsigned char*)sib_buf);
+  for (int k = 0; k < 1024; ++k)
+  {
+    process_data_block(sib_buf[k]);
+  }
+  static __le32 dib_buf[1024];
+  get_block(167583662, (unsigned char*)dib_buf);
+  for (int i = 0; i < 1024; ++i)
+  {
+    assert(dib_buf[i]);
+    get_block(dib_buf[i], (unsigned char*)sib_buf);
+    for (int k = 0; k < 1024; ++k)
+    {
+      process_data_block(sib_buf[k]);
+    }
+  }
+  for (unsigned int j = 0; j < sizeof(tib_table) / sizeof(*tib_table); ++j)
+  {
+    bool saw_zeroes = false;
+    int block_number = tib_table[j];
+    get_dib(block_number, dib_buf);
+    assert(is_indirect_block((unsigned char*)dib_buf));
+    for (int i = 0; i < 1024; ++i)
+    {
+      if (!dib_buf[i])
+	continue;
+      __le32 next_indirect_block;
+      if (i < 1023)
+	next_indirect_block = dib_buf[i + 1];
+      else
+	next_indirect_block = tib_table[j + 1];
+      get_sib(dib_buf[i], sib_buf, next_indirect_block);
+      if (all_zeroes(sib_buf))
+      {
+#if 0
+	std::cout << block_number << " --> " << dib_buf[i] << " == ZEROES!\n";
+	saw_zeroes = true;
+#else
+	assert(false);
+#endif
+      }
+      else
+      {
+	assert(is_indirect_block((unsigned char*)sib_buf, true));
+	if (sib_buf[0] != dib_buf[i] + 1)
+	{
+	  assert(dib_buf[i] + 1 == (__le32)group_to_block(super_block, block_to_group(super_block, sib_buf[0])));
+	}
+	process_data_block(sib_buf[0]);
+	for (int k = 1; k < 1024; ++k)
+	{
+	  if (dib_buf[i] == 83344321 && sib_buf[k] == 0)
+	    continue;
+	  if (sib_buf[k] != sib_buf[k - 1] + 1)
+	  {
+	    if (sib_buf[k - 1] != (__le32)group_to_block(super_block, block_to_group(super_block, sib_buf[k])) - 1)
+	    {
+	      std::cout << block_number << " --> " << dib_buf[i] << " --> " << sib_buf[k] <<
+		  " fails hypothesis. It is the previous entry plus " << (sib_buf[k] - sib_buf[k - 1]) << " instead of plus 1.\n";
+	    }
+	  }
+	  process_data_block(sib_buf[k]);
+	}
+	if (dib_buf[i] == 83344321)
+	  continue;
+	if (sib_buf[1023] + 1 != next_indirect_block)
+	{
+	  int current_group = block_to_group(super_block, sib_buf[1023]);
+	  int next_group = block_to_group(super_block, sib_buf[1023] + 1);
+	  assert(current_group == next_group - 1);
+	}
+      }
+    }
+    if (!saw_zeroes)
+      std::cout << block_number << " OK" << std::endl;
+  }
+  std::cout << "Total number of blocks: " << count << std::endl;
+#if DO_ACTUAL_RECOVERY
+  ::close(outfd);
+#endif
+#endif
+#if 0
+  // This code was used to see if the file that had to be recovered used sparse data
+  // (zero block numbers, meaning a block with only zeroes) and whether or not
+  // blocks where used that were corrupted AFTER the deletion of this file (the
+  // so called 'foremost' blocks).
+  Inode inode;
+  int inode_nr = 34818; // 81362958
+  get_undeleted_inode_type res = get_undeleted_inode(inode_nr, inode);
+  //assert(res == ui_journal_inode);
+  iterate_over_all_blocks_of(inode, inode_nr, custom_action, NULL, direct_bit|indirect_bit, false);
+#endif
+#if 0
+  // This was used to create fib_table2 (and fib_table1 with different loop parameters).
+  // However, fib_table2 was hand editted at a few places after that.
+  std::vector<int> v;
+  for (int group = 2; group <= 2510; ++group)
+  {
+    int res = first_indirect_block(group);
+    v.push_back(res);
+  }
+  int count = 0;
+  for (std::vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
+  {
+    if (count % 16 == 0)
+      std::cout << ",\n  ";
+    else
+      std::cout << ", ";
+    std::cout << *iter;
+    ++count;
+  }
+  std::cout << std::endl;
+  return;
+#endif
+#if 0
+  // This code either tests the heuristic code to generate double indirect blocks,
+  // by comparing what it would do with the actual data of existing blocks,
+  // or it generates such a block; used to generate tables dib_169716665, dib_39617067 etc.
+  for (unsigned int j = 0; j < sizeof(tib_table) / sizeof(*tib_table); ++j)
+  {
+    int block_number = tib_table[j];
+    bool need_restoring =
+        (block_number == 169716665 || block_number == 39617067 || block_number == 49215304 ||
+         block_number == 56681059 || block_number == 66279816);
+    // Toggle this to test the heuristic code, or to generate the missing double indirect blocks.
+    if (!need_restoring)
+      continue;
+    __le32 predicted_indirect_block = block_number + 1;
+    std::cout << block_color(block_number) << ": Double indirect block.\n";
+    __le32 double_block_buf[1024];
+    __le32 indirect_block_buf[1024];
+    get_block(block_number, (unsigned char*)double_block_buf);
+    for (int i = 0; i < 1024; ++i)
+    {
+      if (!need_restoring || double_block_buf[i])
+      {
+	std::cout << '\t' << block_color(double_block_buf[i]) << ": Indirect block.\n";
+	get_block(double_block_buf[i], (unsigned char*)indirect_block_buf);
+	if (all_zeroes(indirect_block_buf))
+	{
+	  std::cout << "\t\tZEROES\n";
+	  assert(double_block_buf[i] == predicted_indirect_block);
+	}
+	else
+	{
+	  assert(is_indirect_block((unsigned char*)indirect_block_buf));
+	  if (double_block_buf[i] != predicted_indirect_block)
+	  {
+	    unsigned char buf[4096];
+	    get_block(predicted_indirect_block, buf);
+	    assert(!is_indirect_block(buf));
+	    predicted_indirect_block += 1025;
+	  }
+	  assert(double_block_buf[i] == predicted_indirect_block);
+	  Range range;
+	  for (int j = 0; j < 1024; ++j)
+	    range += indirect_block_buf[j];
+	  std::cout << "\t\t" << range << '\n';
+	}
+      }
+      else
+      {
+	std::cout << "\tPredicted indirect block: " << predicted_indirect_block << '\n';
+	get_block(predicted_indirect_block, (unsigned char*)indirect_block_buf);
+	if (all_zeroes(indirect_block_buf))
+	{
+	  std::cout << "\t\tZEROES\n";
+	}
+	else
+	{
+	  assert(is_indirect_block((unsigned char*)indirect_block_buf));
+	}
+      }
+      predicted_indirect_block = next_indirect_block(predicted_indirect_block, need_restoring);
+    }
+  }
+#endif
+}
+
diff --git a/src/directories.cc b/src/directories.cc
index 5d4f6ca..c4af13a 100644
--- a/src/directories.cc
+++ b/src/directories.cc
@@ -205,7 +205,7 @@ void iterate_over_directory_action(int blocknr, void* data)
   iterate_over_directory(idata->block_buf, blocknr, idata->action, idata->parent, idata->data);
 }
 
-void iterate_over_existing_directory_action(int blocknr, void* data)
+void iterate_over_existing_directory_action(int blocknr, int, void* data)
 {
   iterate_data_st* idata = reinterpret_cast<iterate_data_st*>(data);
   unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
diff --git a/src/dump_hex_to.cc b/src/dump_hex_to.cc
index fcbc4dd..b2fa4ea 100644
--- a/src/dump_hex_to.cc
+++ b/src/dump_hex_to.cc
@@ -32,11 +32,11 @@
 // dump_hex_to
 //
 
-void dump_hex_to(std::ostream& os, unsigned char const* buf, size_t size)
+void dump_hex_to(std::ostream& os, unsigned char const* buf, size_t size, size_t addr_offset)
 {
   for (size_t addr = 0; addr < size; addr += 16)
   {
-    os << std::hex << std::setfill('0') << std::setw(4) << addr << " |";
+    os << std::hex << std::setfill('0') << std::setw(4) << (addr + addr_offset) << " |";
     int offset;
     for (offset = 0; offset < 16 && addr + offset < size; ++offset)
       os << ' ' << std::hex << std::setfill('0') << std::setw(2) << (int)buf[addr + offset];
diff --git a/src/ext3grep.cc b/src/ext3grep.cc
index bc1fad3..93bd06f 100644
--- a/src/ext3grep.cc
+++ b/src/ext3grep.cc
@@ -230,7 +230,7 @@ void run_program(void)
     if (commandline_print)
     {
       std::cout << "\nHex dump of inode " << commandline_inode << ":\n";
-      dump_hex_to(std::cout, (unsigned char const*)&inode, inode_size_);
+      dump_hex_to(std::cout, (unsigned char const*)&(*inode), inode_size_);
       std::cout << '\n';
     }
     unsigned int bit = commandline_inode - 1 - commandline_group * inodes_per_group_;
@@ -387,9 +387,28 @@ void run_program(void)
 	else
 	{
 	  std::cout << "Block " << commandline_block << " is Unallocated.\n";
-	  ASSERT(!is_inode(commandline_block));	// All inode blocks are allocated.
+	  // If this assertion fails, then it is possible that this DATA block looks like an inode,
+	  // most likely because the data itself is an ext3 filesystem. For example an ext3 image.
+	  // If that is possible, then just comment this assertion out.
+	  //ASSERT(!is_inode(commandline_block));	// All inode blocks are allocated.
 	  ASSERT(!journal);			// All journal blocks are allocated.
 	}
+	if (is_indirect_block(block))
+	{
+	  std::cout << "Block " << commandline_block << " appears to be an (double/tripple) indirect block.\n";
+	  if (commandline_print)
+	  {
+	    std::cout << "It contains the following block numbers:\n";
+	    __le32* block_numbers = reinterpret_cast<__le32*>(block);
+	    for (int i = 0; i < block_size_ >> 2; ++i)
+	    {
+	      std::cout << ' ' << std::setw(9) << std::setfill(' ') << block_numbers[i];
+	      if ((i + 1) % 10 == 0)
+	        std::cout << '\n';
+	    }
+	    std::cout << '\n';
+	  }
+	}
       }
       else
       {
diff --git a/src/forward_declarations.h b/src/forward_declarations.h
index 3efeb8a..ec78ef7 100644
--- a/src/forward_declarations.h
+++ b/src/forward_declarations.h
@@ -35,12 +35,12 @@
 struct Parent;
 class DirectoryBlockStats;
 void decode_commandline_options(int& argc, char**& argv);
-void dump_hex_to(std::ostream& os, unsigned char const* buf, size_t size);
+void dump_hex_to(std::ostream& os, unsigned char const* buf, size_t size, size_t addr_offset = 0);
 void print_block_to(std::ostream& os, unsigned char* block);
 void iterate_over_directory(unsigned char* block, int blocknr,
     bool (*action)(ext3_dir_entry_2 const&, Inode const&, bool, bool, bool, bool, bool, bool, Parent*, void*), Parent* parent, void* data);
 void iterate_over_directory_action(int blocknr, void* data);
-void iterate_over_existing_directory_action(int blocknr, void* data);
+void iterate_over_existing_directory_action(int blocknr, int, void* data);
 void iterate_over_journal(
     bool (*action_tag)(uint32_t block, uint32_t sequence, journal_block_tag_t*, void* data),
     bool (*action_revoke)(uint32_t block, uint32_t sequence, journal_revoke_header_t*, void* data),
diff --git a/src/indirect_blocks.cc b/src/indirect_blocks.cc
index ff07ec0..eb9053d 100644
--- a/src/indirect_blocks.cc
+++ b/src/indirect_blocks.cc
@@ -38,7 +38,11 @@
 //       - Only use an array on the stack if the block numbers are less than the
 //         size of one group apart (instead of allocating and clearing 32 MB on
 //         the stack every time).
-//	- Use return value of std::set<>::insert intead of calling std::set<>::count.
+//	- Use return value of std::set<>::insert instead of calling std::set<>::count.
+//
+// 2008-10-13  Carlo Wood  <carlo at alinoe.com>
+//     * (is_indirect_block):
+//       - SKIPZEROES must be 0: zeroes a completely legal in any indirect block.
 
 #ifndef USE_PCH
 #include "sys.h"
@@ -56,7 +60,7 @@
 // Indirect blocks
 //
 
-void find_block_action(int blocknr, void* ptr)
+void find_block_action(int blocknr, int, void* ptr)
 {
   find_block_data_st& data(*reinterpret_cast<find_block_data_st*>(ptr));
   if (blocknr == data.block_looking_for)
@@ -64,10 +68,10 @@ void find_block_action(int blocknr, void* ptr)
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__find_block_action(void) { find_block_action(0, NULL); }
+void iterate_over_all_blocks_of__with__find_block_action(void) { find_block_action(0, 0, NULL); }
 #endif
 
-void print_directory_action(int blocknr, void*)
+void print_directory_action(int blocknr, int, void*)
 {
   static bool using_static_buffer = false;
   ASSERT(!using_static_buffer);
@@ -81,10 +85,10 @@ void print_directory_action(int blocknr, void*)
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__print_directory_action(void) { print_directory_action(0, NULL); }
+void iterate_over_all_blocks_of__with__print_directory_action(void) { print_directory_action(0, 0, NULL); }
 #endif
 
-bool iterate_over_all_blocks_of_indirect_block(int block, void (*action)(int, void*), void* data, unsigned int, bool diagnose)
+bool iterate_over_all_blocks_of_indirect_block(int block, int& file_block_nr, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
 {
   if (diagnose)
     std::cout << "Processing indirect block " << block << ": " << std::flush;
@@ -93,7 +97,7 @@ bool iterate_over_all_blocks_of_indirect_block(int block, void (*action)(int, vo
   unsigned int i = 0;
   while (i < block_size_ / sizeof(__le32))
   {
-    if (block_ptr[i])
+    if (block_ptr[i] || (indirect_mask & hole_bit))
     {
       if (!is_block_number(block_ptr[i]))
       {
@@ -102,9 +106,10 @@ bool iterate_over_all_blocks_of_indirect_block(int block, void (*action)(int, vo
         break;
       }
       if (!diagnose)
-	action(block_ptr[i], data);
+	action(block_ptr[i], file_block_nr, data);
     }
     ++i;
+    ++file_block_nr;
   }
   bool result = (i < block_size_ / sizeof(__le32));
   if (diagnose && !result)
@@ -112,16 +117,17 @@ bool iterate_over_all_blocks_of_indirect_block(int block, void (*action)(int, vo
   return result;
 }
 
-bool iterate_over_all_blocks_of_double_indirect_block(int block, void (*action)(int, void*), void* data, unsigned int indirect_mask, bool diagnose)
+bool iterate_over_all_blocks_of_double_indirect_block(int block, int& file_block_nr, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
 {
   if (diagnose)
     std::cout << "Start processing double indirect block " << block << '.' << std::endl;
   unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
   __le32* block_ptr = (__le32*)get_block(block, block_buf);
   unsigned int i = 0;
-  while (i < block_size_ / sizeof(__le32))
+  unsigned int const limit = block_size_ >> 2;
+  while (i < limit)
   {
-    if (block_ptr[i])
+    if (block_ptr[i] || (indirect_mask & hole_bit))
     {
       if (!is_block_number(block_ptr[i]))
       {
@@ -130,11 +136,17 @@ bool iterate_over_all_blocks_of_double_indirect_block(int block, void (*action)(
         break;
       }
       if ((indirect_mask & indirect_bit) && !diagnose)
-        action(block_ptr[i], data);
+        action(block_ptr[i], -1, data);
       if ((indirect_mask & direct_bit))
-        if (iterate_over_all_blocks_of_indirect_block(block_ptr[i], action, data, indirect_mask, diagnose))
+      {
+        if (iterate_over_all_blocks_of_indirect_block(block_ptr[i], file_block_nr, action, data, indirect_mask, diagnose))
 	  break;
+      }
+      else
+	file_block_nr += limit;
     }
+    else
+      file_block_nr += limit;
     ++i;
   }
   if (diagnose)
@@ -142,16 +154,17 @@ bool iterate_over_all_blocks_of_double_indirect_block(int block, void (*action)(
   return i < block_size_ / sizeof(__le32);
 }
 
-bool iterate_over_all_blocks_of_tripple_indirect_block(int block, void (*action)(int, void*), void* data, unsigned int indirect_mask, bool diagnose)
+bool iterate_over_all_blocks_of_tripple_indirect_block(int block, int& file_block_nr, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
 {
   if (diagnose)
     std::cout << "Start processing tripple indirect block " << block << '.' << std::endl;
   unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
   __le32* block_ptr = (__le32*)get_block(block, block_buf);
   unsigned int i = 0;
-  while (i < block_size_ / sizeof(__le32))
+  unsigned int const limit = block_size_ >> 2;
+  while (i < limit)
   {
-    if (block_ptr[i])
+    if (block_ptr[i] || (indirect_mask & hole_bit))
     {
       if (!is_block_number(block_ptr[i]))
       {
@@ -160,37 +173,45 @@ bool iterate_over_all_blocks_of_tripple_indirect_block(int block, void (*action)
         break;
       }
       if ((indirect_mask & indirect_bit) && !diagnose)
-        action(block_ptr[i], data);
-      if (iterate_over_all_blocks_of_double_indirect_block(block_ptr[i], action, data, indirect_mask, diagnose))
+        action(block_ptr[i], -1, data);
+      if (iterate_over_all_blocks_of_double_indirect_block(block_ptr[i], file_block_nr, action, data, indirect_mask, diagnose))
         break;
     }
+    else
+      file_block_nr += limit * limit;
     ++i;
   }
   if (diagnose)
     std::cout << "End processing tripple indirect block " << block << '.' << std::endl;
-  return i < block_size_ / sizeof(__le32);
+  return i < limit;
 }
 
 // Returns true if an indirect block was encountered that doesn't look like an indirect block anymore.
-bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*action)(int, void*), void* data, unsigned int indirect_mask, bool diagnose)
+bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*action)(int, int, void*), void* data, unsigned int indirect_mask, bool diagnose)
 {
   if (is_symlink(inode) && inode.blocks() == 0)
     return false;		// Block pointers contain text.
   __le32 const* block_ptr = inode.block();
   if (diagnose)
     std::cout << "Processing direct blocks..." << std::flush;
+  int file_block_nr = 0;
+  unsigned int const limit = block_size_ >> 2;
   if ((indirect_mask & direct_bit))
-    for (int i = 0; i < EXT3_NDIR_BLOCKS; ++i)
-      if (block_ptr[i])
+  {
+    for (int i = 0; i < EXT3_NDIR_BLOCKS; ++i, ++file_block_nr)
+      if (block_ptr[i] || (indirect_mask & hole_bit))
       {
         if (diagnose)
 	  std::cout << ' ' << block_ptr[i] << std::flush;
 	else
-	  action(block_ptr[i], data);
+	  action(block_ptr[i], file_block_nr, data);
       }
+  }
+  else
+    file_block_nr += EXT3_NDIR_BLOCKS;
   if (diagnose)
     std::cout << std::endl;
-  if (block_ptr[EXT3_IND_BLOCK])
+  if (block_ptr[EXT3_IND_BLOCK] || (indirect_mask & hole_bit))
   {
     if (!is_block_number(block_ptr[EXT3_IND_BLOCK]))
     {
@@ -203,12 +224,16 @@ bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*act
       return true;
     }
     if ((indirect_mask & indirect_bit) && !diagnose)
-      action(block_ptr[EXT3_IND_BLOCK], data);
+      action(block_ptr[EXT3_IND_BLOCK], -1, data);
     if ((indirect_mask & direct_bit))
-      if (iterate_over_all_blocks_of_indirect_block(block_ptr[EXT3_IND_BLOCK], action, data, indirect_mask, diagnose))
-        return true;
+    {
+      if (iterate_over_all_blocks_of_indirect_block(block_ptr[EXT3_IND_BLOCK], file_block_nr, action, data, indirect_mask, diagnose))
+	return true;
+    }
   }
-  if (block_ptr[EXT3_DIND_BLOCK])
+  else
+    file_block_nr += limit;
+  if (block_ptr[EXT3_DIND_BLOCK] || (indirect_mask & hole_bit))
   {
     if (!is_block_number(block_ptr[EXT3_DIND_BLOCK]))
     {
@@ -221,11 +246,13 @@ bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*act
       return true;
     }
     if ((indirect_mask & indirect_bit) && !diagnose)
-      action(block_ptr[EXT3_DIND_BLOCK], data);
-    if (iterate_over_all_blocks_of_double_indirect_block(block_ptr[EXT3_DIND_BLOCK], action, data, indirect_mask, diagnose))
+      action(block_ptr[EXT3_DIND_BLOCK], -1, data);
+    if (iterate_over_all_blocks_of_double_indirect_block(block_ptr[EXT3_DIND_BLOCK], file_block_nr, action, data, indirect_mask, diagnose))
       return true;
   }
-  if (block_ptr[EXT3_TIND_BLOCK])
+  else
+    file_block_nr += limit * limit;
+  if (block_ptr[EXT3_TIND_BLOCK] || (indirect_mask & hole_bit))
   {
     if (!is_block_number(block_ptr[EXT3_TIND_BLOCK]))
     {
@@ -238,8 +265,8 @@ bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*act
       return true;
     }
     if ((indirect_mask & indirect_bit) && !diagnose)
-      action(block_ptr[EXT3_TIND_BLOCK], data);
-    if (iterate_over_all_blocks_of_tripple_indirect_block(block_ptr[EXT3_TIND_BLOCK], action, data, indirect_mask, diagnose))
+      action(block_ptr[EXT3_TIND_BLOCK], -1, data);
+    if (iterate_over_all_blocks_of_tripple_indirect_block(block_ptr[EXT3_TIND_BLOCK], file_block_nr, action, data, indirect_mask, diagnose))
       return true;
   }
   return false;
@@ -248,8 +275,10 @@ bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*act
 // See header file for description.
 // Define this to return false if any [bi] is zero, otherwise
 // only false is returned when the first block is zero.
-#define SKIPZEROES 1
-bool is_indirect_block(unsigned char* block_ptr)
+
+// This must be 0.
+#define SKIPZEROES 0
+bool is_indirect_block(unsigned char* block_ptr, bool verbose)
 {
   // Number of 32-bit values per block.
   int const values_per_block = block_size_ / sizeof(__le32);
@@ -272,13 +301,18 @@ bool is_indirect_block(unsigned char* block_ptr)
 #if SKIPZEROES
       if (hasZero)
       {
-        // There already was 0, now it is not 0 --- this is not an indirect block
+	if (verbose)
+	  std::cout << "Found non-zero after zero!" << std::endl;
+        // There already was 0, now it is not 0 --- this might be an indirect block of a file with 'holes'.
+	// However, fail and return false.
         return false;
       }
 #endif
       if (!is_data_block_number(v))
       {
         // This is not a valid block pointer.
+	if (verbose)
+	  std::cout << "Invalid block pointer!" << std::endl;
         return false;
       }
 
@@ -297,7 +331,20 @@ bool is_indirect_block(unsigned char* block_ptr)
   }
 
   if (vmax == 0)
+  {
+    if (verbose)
+    {
+      std::cout << "Block with only zeroes!" << std::endl;
+      std::cout << std::flush;
+      std::cerr << "WARNING: is_indirect_block() was called for a block with ONLY zeroes. "
+	  "The correct return value depends on where we were called from. This is not "
+	  "implemented yet!" << std::endl;
+    }
+    // This should return 'true' if we're called from is_double_indirect_block or is_tripple_indirect_block:
+    // it should not lead to failure namely. In any case, we can definitely not be sure we return the
+    // correct value; a block with only zeroes can theoretically be anything.
     return false;	// Only zeroes.
+  }
 
   // Maximum number of bytes to allocate in an array.
   uint32_t const max_array_size = blocks_per_group(super_block);
@@ -316,6 +363,8 @@ bool is_indirect_block(unsigned char* block_ptr)
       if (t[v - vmin])
       {
         // Value already present!
+	if (verbose)
+	  std::cout << "Duplicated values!" << std::endl;
         return false;
       }
       t[v - vmin] = 1;
@@ -335,7 +384,11 @@ bool is_indirect_block(unsigned char* block_ptr)
       if (!v)
         break;
       if (!bvSet.insert(v).second)	// Was already inserted?
+      {
+	if (verbose)
+	  std::cout << "Duplicated values!" << std::endl;
         return false;
+      }
     }
     return true;
   }
diff --git a/src/indirect_blocks.h b/src/indirect_blocks.h
index 9c66a8d..3958edc 100644
--- a/src/indirect_blocks.h
+++ b/src/indirect_blocks.h
@@ -40,17 +40,18 @@
 // Constants used with iterate_over_all_blocks_of
 unsigned int const direct_bit = 1;		// Call action() for real blocks.
 unsigned int const indirect_bit = 2;		// Call action() for (double/tripple) indirect blocks.
+unsigned int const hole_bit = 4;		// Call action() for holes (blocknr will be 0).
 
-void print_directory_action(int blocknr, void*);
-bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*action)(int, void*), void* data = NULL, unsigned int indirect_mask = direct_bit, bool diagnose = false);
-void find_block_action(int blocknr, void* ptr);
+void print_directory_action(int blocknr, int file_block_nr, void*);
+bool iterate_over_all_blocks_of(Inode const& inode, int inode_number, void (*action)(int, int, void*), void* data = NULL, unsigned int indirect_mask = direct_bit, bool diagnose = false);
+void find_block_action(int blocknr, int file_block_nr, void* ptr);
 
 struct find_block_data_st {
   bool found_block;
   int block_looking_for;
 };
 
-inline bool iterate_over_all_blocks_of(InodePointer inode, int inode_number, void (*action)(int, void*), void* data = NULL,
+inline bool iterate_over_all_blocks_of(InodePointer inode, int inode_number, void (*action)(int, int, void*), void* data = NULL,
     unsigned int indirect_mask = direct_bit, bool diagnose = false)
 {
   // inode is dereferenced here in good faith that no reference to it is kept (since there are no structs or classes that do so).
@@ -77,6 +78,6 @@ inline bool iterate_over_all_blocks_of(InodePointer inode, int inode_number, voi
  *    - [bi] are all different.
  *    - [bi] != 0 for all i.
  */
-bool is_indirect_block(unsigned char* block_ptr);
+bool is_indirect_block(unsigned char* block_ptr, bool verbose = false);
 
 #endif // INDIRECT_BLOCKS_H
diff --git a/src/inode_refers_to.cc b/src/inode_refers_to.cc
index 6a54253..b827530 100644
--- a/src/inode_refers_to.cc
+++ b/src/inode_refers_to.cc
@@ -34,7 +34,7 @@ struct inode_refers_to_st
   bool found;
 };
 
-void inode_refers_to_action(int blocknr, void* ptr)
+void inode_refers_to_action(int blocknr, int, void* ptr)
 {
   inode_refers_to_st& data(*reinterpret_cast<inode_refers_to_st*>(ptr));
   if (blocknr == data.block_number)
@@ -42,7 +42,7 @@ void inode_refers_to_action(int blocknr, void* ptr)
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__inode_refers_to_action(void) { inode_refers_to_action(0, NULL); }
+void iterate_over_all_blocks_of__with__inode_refers_to_action(void) { inode_refers_to_action(0, 0, NULL); }
 #endif
 
 bool inode_refers_to(Inode const& inode, int inode_number, int block_number)
diff --git a/src/journal.cc b/src/journal.cc
index 2d85a7a..891b021 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -314,7 +314,7 @@ static int min_journal_block;
 static int max_journal_block;		// One more than largest block belonging to the journal.
 static bitmap_t* is_indirect_block_in_journal_bitmap = NULL;
 
-void find_blocknr_range_action(int blocknr, void*)
+void find_blocknr_range_action(int blocknr, int, void*)
 {
   if (blocknr > largest_block_nr)
     largest_block_nr = blocknr;
@@ -323,30 +323,30 @@ void find_blocknr_range_action(int blocknr, void*)
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__find_blocknr_range_action(void) { find_blocknr_range_action(0, NULL); }
+void iterate_over_all_blocks_of__with__find_blocknr_range_action(void) { find_blocknr_range_action(0, 0, NULL); }
 #endif
 
-void fill_journal_bitmap_action(int blocknr, void*)
+void fill_journal_bitmap_action(int blocknr, int, void*)
 {
   bitmap_ptr bmp = get_bitmap_mask(blocknr - min_journal_block);
   journal_block_bitmap[bmp.index] |= bmp.mask;
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__fill_journal_bitmap_action(void) { fill_journal_bitmap_action(0, NULL); }
+void iterate_over_all_blocks_of__with__fill_journal_bitmap_action(void) { fill_journal_bitmap_action(0, 0, NULL); }
 #endif
 
-void indirect_journal_block_action(int blocknr, void*)
+void indirect_journal_block_action(int blocknr, int, void*)
 {
   bitmap_ptr bmp = get_bitmap_mask(blocknr - min_journal_block);
   is_indirect_block_in_journal_bitmap[bmp.index] |= bmp.mask;
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__indirect_journal_block_action(void) { indirect_journal_block_action(0, NULL); }
+void iterate_over_all_blocks_of__with__indirect_journal_block_action(void) { indirect_journal_block_action(0, 0, NULL); }
 #endif
 
-void directory_inode_action(int blocknr, void* data)
+void directory_inode_action(int blocknr, int, void* data)
 {
   int inode_number = *reinterpret_cast<int*>(data);
   block_to_dir_inode_map_type::iterator iter = block_to_dir_inode_map.find(blocknr);
@@ -357,7 +357,7 @@ void directory_inode_action(int blocknr, void* data)
 }
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__directory_inode_action(void) { directory_inode_action(0, NULL); }
+void iterate_over_all_blocks_of__with__directory_inode_action(void) { directory_inode_action(0, 0, NULL); }
 #endif
 
 void init_journal(void)
@@ -501,7 +501,7 @@ void init_journal(void)
       // Normally a lasttime != 0 should do. But I ran into a case where the supposedly inode block
       // didn't contain inodes at all, but block numbers?! Therefore, check that lasttime > inode_count_,
       // which will be the case in 99.999% of the cases for a real time_t.
-      if (lasttime > inode_count_ && (__le32_to_cpu(lasttime) < oldtime || oldtime == 0))
+      if ((uint32_t)lasttime > inode_count_ && (__le32_to_cpu(lasttime) < (uint32_t)oldtime || oldtime == 0))
 	oldtime = __le32_to_cpu(lasttime);
     }
   }
diff --git a/src/locate.cc b/src/locate.cc
index 573d8a5..2b8f705 100644
--- a/src/locate.cc
+++ b/src/locate.cc
@@ -168,7 +168,6 @@ std::string parent_directory(int
     // You can set test_blocknr and uncomment the #if 0'd code below to debug your regular expressions.
     static struct { char const* regexp; char const* path; } table[] = {
 //#ifdef CARLO_WOODS_CASE	This should be automatic now.
-//      { "^(.*-BNC|ircproxy-.*|EFnet|UnderNet|FreeNode .*|OFTC|NETWORK|GimpNet|AS-....)-.*\\.log$", "carlo/.xchat2/xchatlogs" },
 //      { "^([0-9]{10}-[0-9]{3,5}-[0-9]+|11c0a8020[0-9]{28})\\.ms$", "carlo/k3b/temp" },
 //      { "^1[12][0-9]{11}_(AutoSpeedSearchHistory|SpeedMan|seltrace|thread|alerts|debug)_[12]\\.log$", "carlo/.azureus/logs/save" },
 //      { "^opr0[0-9][0-9A-Z]{3}\\.(js|ico|htm|gif|png|html|jpeg|xml|flv|css|swf|jpg)$", "lost+found" },
diff --git a/src/ostream_operators.cc b/src/ostream_operators.cc
index 0aa81a9..be1d5b0 100644
--- a/src/ostream_operators.cc
+++ b/src/ostream_operators.cc
@@ -243,13 +243,17 @@ std::ostream& operator<<(std::ostream& os, journal_revoke_header_t const& journa
   count /= sizeof(__be32);
   __be32 const* ptr = reinterpret_cast<__be32 const*>((unsigned char const*)&journal_revoke_header + sizeof(journal_revoke_header_t));
   int c = 0;
+  if (count > 0)
+    std::cout << "Revoked blocks:\n";
   for (uint32_t b = 0; b < count; ++b)
   {
-    std::cout << std::setfill(' ') << std::setw(8) << be2le(ptr[b]);
+    std::cout << std::setfill(' ') << std::setw(9) << be2le(ptr[b]);
     ++c;
     c &= 7;
     if (c == 0)
       std::cout << '\n';
+    else
+      std::cout << ' ';
   }
   return os;
 }
diff --git a/src/print_inode_to.cc b/src/print_inode_to.cc
index 66e8a3e..ef5f2af 100644
--- a/src/print_inode_to.cc
+++ b/src/print_inode_to.cc
@@ -93,15 +93,22 @@ void print_inode_to(std::ostream& os, Inode const& inode)
   if ((inode.mode() & 0xf000) != 0xa000 || inode.blocks() != 0)		// Not an inline symlink?
   {
     os << "\nDirect Blocks:";
+    long sb = (inode.size() + block_size_ - 1) / block_size_;	// Size in blocks.
     for (int n = 0; n < EXT3_NDIR_BLOCKS; ++n)
-      if (inode.block()[n])
-	os << ' ' << inode.block()[n];
+    {
+      os << ' ' << inode.block()[n];
+      --sb;
+      if (sb <= 0)
+	break;
+    }
     os << '\n';
-    if (inode.block()[EXT3_IND_BLOCK])
+    if (sb > 0)
       os << "Indirect Block: " << inode.block()[EXT3_IND_BLOCK] << '\n';
-    if (inode.block()[EXT3_DIND_BLOCK])
+    sb -= block_size_ >> 2;
+    if (sb > 0)
       os << "Double Indirect Block: " << inode.block()[EXT3_DIND_BLOCK] << '\n';
-    if (inode.block()[EXT3_TIND_BLOCK])
+    sb -= (block_size_ >> 2) * (block_size_ >> 2);
+    if (sb > 0)
       os << "Tripple Indirect Block: " << inode.block()[EXT3_TIND_BLOCK] << '\n';
   }
   else
diff --git a/src/printing.cc b/src/printing.cc
index a09bc5e..c862258 100644
--- a/src/printing.cc
+++ b/src/printing.cc
@@ -37,7 +37,24 @@
 
 void print_block_to(std::ostream& os, unsigned char* block)
 {
-  dump_hex_to(os, block, block_size_);
+  unsigned char buf[16];
+  size_t offset = 0;
+  bool last_was_star = false;
+  for (unsigned char* p = block; p < block + block_size_; p += 16, offset += 16)
+  {
+    if (offset > 0 && offset + 16 < block_size_ && memcmp(buf, p, 16) == 0)
+    {
+      if (!last_was_star)
+      {
+	os << " *\n";
+	last_was_star = true;
+      }
+      continue;
+    }
+    dump_hex_to(os, p, 16, offset);
+    memcpy(buf, p, 16);
+    last_was_star = false;
+  }
 }
 
 void print_restrictions(void)
diff --git a/src/restore.cc b/src/restore.cc
index 9222c30..585c51f 100644
--- a/src/restore.cc
+++ b/src/restore.cc
@@ -46,17 +46,10 @@
 #include "print_symlink.h"
 
 #ifdef CPPGRAPH
-void iterate_over_all_blocks_of__with__restore_file_action(void) { restore_file_action(0, NULL); }
+void iterate_over_all_blocks_of__with__restore_file_action(void) { restore_file_action(0, 0, NULL); }
 #endif
 
-enum get_undeleted_inode_type {
-  ui_no_inode,
-  ui_real_inode,
-  ui_journal_inode,
-  ui_inode_too_old
-};
-
-get_undeleted_inode_type get_undeleted_inode(int inodenr, Inode& inode, int* sequence = NULL)
+get_undeleted_inode_type get_undeleted_inode(int inodenr, Inode& inode, int* sequence)
 {
   InodePointer real_inode(get_inode(inodenr));
   if (!real_inode->is_deleted())
@@ -85,25 +78,46 @@ get_undeleted_inode_type get_undeleted_inode(int inodenr, Inode& inode, int* seq
 extern "C" int lutimes (char const*, struct timeval const [2]);
 
 struct Data {
-  std::ostream& out;
+  int out;
   off_t remaining_size;
+  int expected_file_block_nr;
 
-  Data(std::ostream& out_, off_t remaining_size_) : out(out_), remaining_size(remaining_size_) { }
+  Data(int out_, off_t remaining_size_) : out(out_), remaining_size(remaining_size_), expected_file_block_nr(0) { }
 };
 
-void restore_file_action(int blocknr, void* ptr)
+void restore_file_action(int blocknr, int file_block_nr, void* ptr)
 {
   Data& data(*reinterpret_cast<Data*>(ptr));
   static unsigned char block_buf[EXT3_MAX_BLOCK_SIZE];
   int len;
 
+  if (data.expected_file_block_nr != file_block_nr)
+  {
+    ASSERT(data.expected_file_block_nr != -1);	// It's set to -1 below when we reached the end of the file.
+    off64_t pos = ((off64_t) file_block_nr) * block_size_;
+    if (lseek64(data.out, pos, SEEK_SET) == (off_t) -1)
+    {
+      int error = errno;
+      std::cout << std::flush;
+      std::cerr << progname << "restore_file_action: could not lseek64 to position " << pos << ": " << strerror(error) << std::endl;
+      exit(EXIT_FAILURE);
+    }
+    data.expected_file_block_nr = file_block_nr;
+  }
+
   get_block(blocknr, block_buf);
   if (data.remaining_size > block_size_)
+  {
     len = block_size_;
+    data.expected_file_block_nr += block_size_;
+  }
   else
+  {
     len = data.remaining_size;
-  data.out.write((char const*)block_buf, len);
-  ASSERT(data.out.good());
+    data.expected_file_block_nr = -1;	// This was the last block.
+  }
+  int res = ::write(data.out, (char const*)block_buf, len);
+  ASSERT(res == len);
   data.remaining_size -= len;
 }
 
@@ -171,6 +185,12 @@ void restore_inode(int inodenr, InodePointer real_inode, std::string const& outf
       std::cerr << progname << ": could not create directory " << outputdir_outfile << ": " << strerror(error) << std::endl;
       exit(EXIT_FAILURE);
     }
+    if (chmod(outputdir_outfile.c_str(), mode) == -1)
+    {
+      int error = errno;
+      std::cout << "WARNING: failed to set mode on directory " << outputdir_outfile << std::endl;
+      std::cerr << progname << ": chmod: " << strerror(error) << std::endl;
+    }
     struct utimbuf ub;
     ub.actime = real_inode->atime();
     ub.modtime = real_inode->mtime();
@@ -195,9 +215,9 @@ void restore_inode(int inodenr, InodePointer real_inode, std::string const& outf
     ASSERT(!inode.is_deleted());
     if (is_regular_file(inode))
     {
-      std::ofstream out;
-      out.open(outputdir_outfile.c_str());
-      if (!out)
+      int out;
+      out = ::open(outputdir_outfile.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0777);
+      if (out == -1)
       {
 	std::cout << "Failed to open \"" << outputdir_outfile << "\".\n";
 	return;
@@ -209,8 +229,7 @@ void restore_inode(int inodenr, InodePointer real_inode, std::string const& outf
       iterate_over_all_blocks_of__with__restore_file_action();
 #endif
       bool reused_or_corrupted_indirect_block8 = iterate_over_all_blocks_of(inode, inodenr, restore_file_action, &data);
-      ASSERT(out.good());
-      out.close();
+      ::close(out);
       if (reused_or_corrupted_indirect_block8)
       {
         std::cout << "WARNING: Failed to restore " << outfile << ": encountered a reused or corrupted (double/triple) indirect block!\n";
diff --git a/src/restore.h b/src/restore.h
index e7e83a6..65b51c2 100644
--- a/src/restore.h
+++ b/src/restore.h
@@ -35,4 +35,13 @@ std::string const outputdir = "RESTORED_FILES/";
 
 void restore_inode(int inodenr, InodePointer real_inode, std::string const& outfile);
 
+enum get_undeleted_inode_type {
+  ui_no_inode,
+  ui_real_inode,
+  ui_journal_inode,
+  ui_inode_too_old
+};
+
+get_undeleted_inode_type get_undeleted_inode(int inodenr, Inode& inode, int* sequence = NULL);
+
 #endif // RESTORE_H
diff --git a/src/revision.cc b/src/revision.cc
index f9a2b11..9acbe03 100644
--- a/src/revision.cc
+++ b/src/revision.cc
@@ -1 +1 @@
-char const* svn_revision = "Revision: 115";
+char const* svn_revision = "Revision: 121";
diff --git a/src/sys.h.in b/src/sys.h.in
index 47763a3..11fbc8d 100644
--- a/src/sys.h.in
+++ b/src/sys.h.in
@@ -30,6 +30,9 @@
 #include "config.h"
 #endif
 
+// This is needed for lseek64.
+#define _LARGEFILE64_SOURCE
+
 #ifdef CWDEBUG
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE

-- 
Tool to help recover deleted files on ext3 filesystems



More information about the forensics-changes mailing list