[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(¤t_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