diff options
author | robi <robi> | 2010-05-20 00:15:01 +0000 |
---|---|---|
committer | robi <robi> | 2010-05-20 00:15:01 +0000 |
commit | 4efdf36026aa2c76ea8a0b8667598e49bde7b678 (patch) | |
tree | de14b911cf2fb2e77a8913b9fdce3c3348320ceb /src |
Initial revision
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 14 | ||||
-rw-r--r-- | src/Makefile.in | 753 | ||||
-rw-r--r-- | src/block.c | 650 | ||||
-rw-r--r-- | src/block.h | 26 | ||||
-rw-r--r-- | src/dir_list.c | 184 | ||||
-rw-r--r-- | src/dir_list.h | 61 | ||||
-rw-r--r-- | src/ext2fsP.h | 87 | ||||
-rw-r--r-- | src/ext4magic.8 | 512 | ||||
-rw-r--r-- | src/ext4magic.c | 900 | ||||
-rw-r--r-- | src/ext4magic.h | 45 | ||||
-rw-r--r-- | src/hard_link_stack.c | 132 | ||||
-rw-r--r-- | src/hard_link_stack.h | 49 | ||||
-rw-r--r-- | src/inode.c | 805 | ||||
-rw-r--r-- | src/inode.h | 69 | ||||
-rw-r--r-- | src/jfs_compat.h | 70 | ||||
-rw-r--r-- | src/jfs_user.h | 8 | ||||
-rw-r--r-- | src/journal.c | 759 | ||||
-rw-r--r-- | src/journal.h | 47 | ||||
-rw-r--r-- | src/kernel-jbd.h | 951 | ||||
-rw-r--r-- | src/kernel-list.h | 112 | ||||
-rw-r--r-- | src/lookup_local.c | 732 | ||||
-rw-r--r-- | src/recover.c | 583 | ||||
-rw-r--r-- | src/ring_buf.c | 103 | ||||
-rw-r--r-- | src/ring_buf.h | 75 | ||||
-rw-r--r-- | src/util.c | 442 | ||||
-rw-r--r-- | src/util.h | 106 |
26 files changed, 8275 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..40c7242 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,14 @@ +sbin_PROGRAMS = ext4magic +ext4magic_SOURCES = block.c dir_list.c ext4magic.c \ + hard_link_stack.c inode.c journal.c lookup_local.c recover.c ring_buf.c util.c + +# set the include path found by configure +INCLUDES= $(all_includes) + +man_MANS = ext4magic.8 + +# the library search path. +ext4magic_CFLAGS = -O2 -g -D_FILE_OFFSET_BITS=64 +ext4magic_LDFLAGS = -lext2fs -le2p -luuid -lblkid +noinst_HEADERS = block.h dir_list.h ext2fsP.h kernel-list.h ext4magic.h hard_link_stack.h \ + inode.h jfs_compat.h jfs_user.h journal.h kernel-jbd.h ring_buf.h util.h ext4magic.8 diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..7391b9f --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,753 @@ +# Makefile.in generated by automake 1.11 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +sbin_PROGRAMS = ext4magic$(EXEEXT) +subdir = src +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" +PROGRAMS = $(sbin_PROGRAMS) +am_ext4magic_OBJECTS = ext4magic-block.$(OBJEXT) \ + ext4magic-dir_list.$(OBJEXT) ext4magic-ext4magic.$(OBJEXT) \ + ext4magic-hard_link_stack.$(OBJEXT) ext4magic-inode.$(OBJEXT) \ + ext4magic-journal.$(OBJEXT) ext4magic-lookup_local.$(OBJEXT) \ + ext4magic-recover.$(OBJEXT) ext4magic-ring_buf.$(OBJEXT) \ + ext4magic-util.$(OBJEXT) +ext4magic_OBJECTS = $(am_ext4magic_OBJECTS) +ext4magic_LDADD = $(LDADD) +ext4magic_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ext4magic_CFLAGS) \ + $(CFLAGS) $(ext4magic_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(ext4magic_SOURCES) +DIST_SOURCES = $(ext4magic_SOURCES) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +man8dir = $(mandir)/man8 +NROFF = nroff +MANS = $(man_MANS) +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ext4magic_SOURCES = block.c dir_list.c ext4magic.c \ + hard_link_stack.c inode.c journal.c lookup_local.c recover.c ring_buf.c util.c + + +# set the include path found by configure +INCLUDES = $(all_includes) +man_MANS = ext4magic.8 + +# the library search path. +ext4magic_CFLAGS = -O2 -g -D_FILE_OFFSET_BITS=64 +ext4magic_LDFLAGS = -lext2fs -le2p -luuid -lblkid +noinst_HEADERS = block.h dir_list.h ext2fsP.h kernel-list.h ext4magic.h hard_link_stack.h \ + inode.h jfs_compat.h jfs_user.h journal.h kernel-jbd.h ring_buf.h util.h ext4magic.8 + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +ext4magic$(EXEEXT): $(ext4magic_OBJECTS) $(ext4magic_DEPENDENCIES) + @rm -f ext4magic$(EXEEXT) + $(ext4magic_LINK) $(ext4magic_OBJECTS) $(ext4magic_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-block.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-dir_list.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-ext4magic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-hard_link_stack.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-inode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-journal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-lookup_local.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-recover.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-ring_buf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext4magic-util.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +ext4magic-block.o: block.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-block.o -MD -MP -MF $(DEPDIR)/ext4magic-block.Tpo -c -o ext4magic-block.o `test -f 'block.c' || echo '$(srcdir)/'`block.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-block.Tpo $(DEPDIR)/ext4magic-block.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='block.c' object='ext4magic-block.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-block.o `test -f 'block.c' || echo '$(srcdir)/'`block.c + +ext4magic-block.obj: block.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-block.obj -MD -MP -MF $(DEPDIR)/ext4magic-block.Tpo -c -o ext4magic-block.obj `if test -f 'block.c'; then $(CYGPATH_W) 'block.c'; else $(CYGPATH_W) '$(srcdir)/block.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-block.Tpo $(DEPDIR)/ext4magic-block.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='block.c' object='ext4magic-block.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-block.obj `if test -f 'block.c'; then $(CYGPATH_W) 'block.c'; else $(CYGPATH_W) '$(srcdir)/block.c'; fi` + +ext4magic-dir_list.o: dir_list.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-dir_list.o -MD -MP -MF $(DEPDIR)/ext4magic-dir_list.Tpo -c -o ext4magic-dir_list.o `test -f 'dir_list.c' || echo '$(srcdir)/'`dir_list.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-dir_list.Tpo $(DEPDIR)/ext4magic-dir_list.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dir_list.c' object='ext4magic-dir_list.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-dir_list.o `test -f 'dir_list.c' || echo '$(srcdir)/'`dir_list.c + +ext4magic-dir_list.obj: dir_list.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-dir_list.obj -MD -MP -MF $(DEPDIR)/ext4magic-dir_list.Tpo -c -o ext4magic-dir_list.obj `if test -f 'dir_list.c'; then $(CYGPATH_W) 'dir_list.c'; else $(CYGPATH_W) '$(srcdir)/dir_list.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-dir_list.Tpo $(DEPDIR)/ext4magic-dir_list.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dir_list.c' object='ext4magic-dir_list.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-dir_list.obj `if test -f 'dir_list.c'; then $(CYGPATH_W) 'dir_list.c'; else $(CYGPATH_W) '$(srcdir)/dir_list.c'; fi` + +ext4magic-ext4magic.o: ext4magic.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-ext4magic.o -MD -MP -MF $(DEPDIR)/ext4magic-ext4magic.Tpo -c -o ext4magic-ext4magic.o `test -f 'ext4magic.c' || echo '$(srcdir)/'`ext4magic.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-ext4magic.Tpo $(DEPDIR)/ext4magic-ext4magic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ext4magic.c' object='ext4magic-ext4magic.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-ext4magic.o `test -f 'ext4magic.c' || echo '$(srcdir)/'`ext4magic.c + +ext4magic-ext4magic.obj: ext4magic.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-ext4magic.obj -MD -MP -MF $(DEPDIR)/ext4magic-ext4magic.Tpo -c -o ext4magic-ext4magic.obj `if test -f 'ext4magic.c'; then $(CYGPATH_W) 'ext4magic.c'; else $(CYGPATH_W) '$(srcdir)/ext4magic.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-ext4magic.Tpo $(DEPDIR)/ext4magic-ext4magic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ext4magic.c' object='ext4magic-ext4magic.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-ext4magic.obj `if test -f 'ext4magic.c'; then $(CYGPATH_W) 'ext4magic.c'; else $(CYGPATH_W) '$(srcdir)/ext4magic.c'; fi` + +ext4magic-hard_link_stack.o: hard_link_stack.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-hard_link_stack.o -MD -MP -MF $(DEPDIR)/ext4magic-hard_link_stack.Tpo -c -o ext4magic-hard_link_stack.o `test -f 'hard_link_stack.c' || echo '$(srcdir)/'`hard_link_stack.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-hard_link_stack.Tpo $(DEPDIR)/ext4magic-hard_link_stack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hard_link_stack.c' object='ext4magic-hard_link_stack.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-hard_link_stack.o `test -f 'hard_link_stack.c' || echo '$(srcdir)/'`hard_link_stack.c + +ext4magic-hard_link_stack.obj: hard_link_stack.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-hard_link_stack.obj -MD -MP -MF $(DEPDIR)/ext4magic-hard_link_stack.Tpo -c -o ext4magic-hard_link_stack.obj `if test -f 'hard_link_stack.c'; then $(CYGPATH_W) 'hard_link_stack.c'; else $(CYGPATH_W) '$(srcdir)/hard_link_stack.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-hard_link_stack.Tpo $(DEPDIR)/ext4magic-hard_link_stack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hard_link_stack.c' object='ext4magic-hard_link_stack.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-hard_link_stack.obj `if test -f 'hard_link_stack.c'; then $(CYGPATH_W) 'hard_link_stack.c'; else $(CYGPATH_W) '$(srcdir)/hard_link_stack.c'; fi` + +ext4magic-inode.o: inode.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-inode.o -MD -MP -MF $(DEPDIR)/ext4magic-inode.Tpo -c -o ext4magic-inode.o `test -f 'inode.c' || echo '$(srcdir)/'`inode.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-inode.Tpo $(DEPDIR)/ext4magic-inode.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='inode.c' object='ext4magic-inode.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-inode.o `test -f 'inode.c' || echo '$(srcdir)/'`inode.c + +ext4magic-inode.obj: inode.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-inode.obj -MD -MP -MF $(DEPDIR)/ext4magic-inode.Tpo -c -o ext4magic-inode.obj `if test -f 'inode.c'; then $(CYGPATH_W) 'inode.c'; else $(CYGPATH_W) '$(srcdir)/inode.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-inode.Tpo $(DEPDIR)/ext4magic-inode.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='inode.c' object='ext4magic-inode.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-inode.obj `if test -f 'inode.c'; then $(CYGPATH_W) 'inode.c'; else $(CYGPATH_W) '$(srcdir)/inode.c'; fi` + +ext4magic-journal.o: journal.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-journal.o -MD -MP -MF $(DEPDIR)/ext4magic-journal.Tpo -c -o ext4magic-journal.o `test -f 'journal.c' || echo '$(srcdir)/'`journal.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-journal.Tpo $(DEPDIR)/ext4magic-journal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='journal.c' object='ext4magic-journal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-journal.o `test -f 'journal.c' || echo '$(srcdir)/'`journal.c + +ext4magic-journal.obj: journal.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-journal.obj -MD -MP -MF $(DEPDIR)/ext4magic-journal.Tpo -c -o ext4magic-journal.obj `if test -f 'journal.c'; then $(CYGPATH_W) 'journal.c'; else $(CYGPATH_W) '$(srcdir)/journal.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-journal.Tpo $(DEPDIR)/ext4magic-journal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='journal.c' object='ext4magic-journal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-journal.obj `if test -f 'journal.c'; then $(CYGPATH_W) 'journal.c'; else $(CYGPATH_W) '$(srcdir)/journal.c'; fi` + +ext4magic-lookup_local.o: lookup_local.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-lookup_local.o -MD -MP -MF $(DEPDIR)/ext4magic-lookup_local.Tpo -c -o ext4magic-lookup_local.o `test -f 'lookup_local.c' || echo '$(srcdir)/'`lookup_local.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-lookup_local.Tpo $(DEPDIR)/ext4magic-lookup_local.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lookup_local.c' object='ext4magic-lookup_local.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-lookup_local.o `test -f 'lookup_local.c' || echo '$(srcdir)/'`lookup_local.c + +ext4magic-lookup_local.obj: lookup_local.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-lookup_local.obj -MD -MP -MF $(DEPDIR)/ext4magic-lookup_local.Tpo -c -o ext4magic-lookup_local.obj `if test -f 'lookup_local.c'; then $(CYGPATH_W) 'lookup_local.c'; else $(CYGPATH_W) '$(srcdir)/lookup_local.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-lookup_local.Tpo $(DEPDIR)/ext4magic-lookup_local.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lookup_local.c' object='ext4magic-lookup_local.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-lookup_local.obj `if test -f 'lookup_local.c'; then $(CYGPATH_W) 'lookup_local.c'; else $(CYGPATH_W) '$(srcdir)/lookup_local.c'; fi` + +ext4magic-recover.o: recover.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-recover.o -MD -MP -MF $(DEPDIR)/ext4magic-recover.Tpo -c -o ext4magic-recover.o `test -f 'recover.c' || echo '$(srcdir)/'`recover.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-recover.Tpo $(DEPDIR)/ext4magic-recover.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='recover.c' object='ext4magic-recover.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-recover.o `test -f 'recover.c' || echo '$(srcdir)/'`recover.c + +ext4magic-recover.obj: recover.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-recover.obj -MD -MP -MF $(DEPDIR)/ext4magic-recover.Tpo -c -o ext4magic-recover.obj `if test -f 'recover.c'; then $(CYGPATH_W) 'recover.c'; else $(CYGPATH_W) '$(srcdir)/recover.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-recover.Tpo $(DEPDIR)/ext4magic-recover.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='recover.c' object='ext4magic-recover.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-recover.obj `if test -f 'recover.c'; then $(CYGPATH_W) 'recover.c'; else $(CYGPATH_W) '$(srcdir)/recover.c'; fi` + +ext4magic-ring_buf.o: ring_buf.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-ring_buf.o -MD -MP -MF $(DEPDIR)/ext4magic-ring_buf.Tpo -c -o ext4magic-ring_buf.o `test -f 'ring_buf.c' || echo '$(srcdir)/'`ring_buf.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-ring_buf.Tpo $(DEPDIR)/ext4magic-ring_buf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ring_buf.c' object='ext4magic-ring_buf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-ring_buf.o `test -f 'ring_buf.c' || echo '$(srcdir)/'`ring_buf.c + +ext4magic-ring_buf.obj: ring_buf.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-ring_buf.obj -MD -MP -MF $(DEPDIR)/ext4magic-ring_buf.Tpo -c -o ext4magic-ring_buf.obj `if test -f 'ring_buf.c'; then $(CYGPATH_W) 'ring_buf.c'; else $(CYGPATH_W) '$(srcdir)/ring_buf.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-ring_buf.Tpo $(DEPDIR)/ext4magic-ring_buf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ring_buf.c' object='ext4magic-ring_buf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-ring_buf.obj `if test -f 'ring_buf.c'; then $(CYGPATH_W) 'ring_buf.c'; else $(CYGPATH_W) '$(srcdir)/ring_buf.c'; fi` + +ext4magic-util.o: util.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-util.o -MD -MP -MF $(DEPDIR)/ext4magic-util.Tpo -c -o ext4magic-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-util.Tpo $(DEPDIR)/ext4magic-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='util.c' object='ext4magic-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-util.o `test -f 'util.c' || echo '$(srcdir)/'`util.c + +ext4magic-util.obj: util.c +@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -MT ext4magic-util.obj -MD -MP -MF $(DEPDIR)/ext4magic-util.Tpo -c -o ext4magic-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/ext4magic-util.Tpo $(DEPDIR)/ext4magic-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='util.c' object='ext4magic-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ext4magic_CFLAGS) $(CFLAGS) -c -o ext4magic-util.obj `if test -f 'util.c'; then $(CYGPATH_W) 'util.c'; else $(CYGPATH_W) '$(srcdir)/util.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-man8: $(man_MANS) + @$(NORMAL_INSTALL) + test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)" + @list=''; test -n "$(man8dir)" || exit 0; \ + { for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | while read p; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; echo "$$p"; \ + done | \ + sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ + sed 'N;N;s,\n, ,g' | { \ + list=; while read file base inst; do \ + if test "$$base" = "$$inst"; then list="$$list $$file"; else \ + echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ + $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ + fi; \ + done; \ + for i in $$list; do echo "$$i"; done | $(am__base_list) | \ + while read files; do \ + test -z "$$files" || { \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ + done; } + +uninstall-man8: + @$(NORMAL_UNINSTALL) + @list=''; test -n "$(man8dir)" || exit 0; \ + files=`{ for i in $$list; do echo "$$i"; done; \ + l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ + sed -n '/\.8[a-z]*$$/p'; \ + } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ + -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ + test -z "$$files" || { \ + echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(man8dir)" && rm -f $$files; } + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @list='$(MANS)'; if test -n "$$list"; then \ + list=`for p in $$list; do \ + if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \ + if test -n "$$list" && \ + grep 'ab help2man is required to generate this page' $$list >/dev/null; then \ + echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \ + grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \ + echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \ + echo " typically \`make maintainer-clean' will remove them" >&2; \ + exit 1; \ + else :; fi; \ + else :; fi + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(MANS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-man + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: install-man8 + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-man uninstall-sbinPROGRAMS + +uninstall-man: uninstall-man8 + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-sbinPROGRAMS ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-man uninstall-man8 \ + uninstall-sbinPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/block.c b/src/block.c new file mode 100644 index 0000000..fd60df7 --- /dev/null +++ b/src/block.c @@ -0,0 +1,650 @@ +/* + * This file was modified from e2fsprogs 1.41.4 + * Use this file when compiling against a newer version of ext2fs headers. + * block.c --- iterate over all blocks in an inode + * extent.c --- work with extents + * + * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +/* + This is a workaround to allow compilation, but the one line that uses + this constant will never run because we open the fs read-only. +*/ +#define EXT4INO 0 + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifndef EXT2_FLAT_INCLUDES +#define EXT2_FLAT_INCLUDES 0 +#endif + +//#include <ext2fs/ext2fs.h> +#include "ext2fsP.h" +#include "block.h" + +struct block_context { + ext2_filsys fs; + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t bcount, + blk_t ref_blk, + int ref_offset, + void *priv_data); + e2_blkcnt_t bcount; + int bsize; + int flags; + errcode_t errcode; + char *ind_buf; + char *dind_buf; + char *tind_buf; + void *priv_data; +}; + + +//#ifdef BLOCK_FLAG_READ_ONLY + +#include <ext2fs/ext3_extents.h> +struct extent_path { + char *buf; + int entries; + int max_entries; + int left; + int visit_num; + int flags; + blk64_t end_blk; + void *curr; +}; + + +struct ext2_extent_handle { + errcode_t magic; + ext2_filsys fs; + ext2_ino_t ino; + struct ext2_inode *inode; + int type; + int level; + int max_depth; + struct extent_path *path; +}; + +// Leave the inode intact because it was allocated by new (C++) +void local_ext2fs_extent_free(ext2_extent_handle_t handle) +{ + int i; + + if (!handle) + return; + + //if (handle->inode) + // ext2fs_free_mem(&handle->inode); + if (handle->path) { + for (i=1; i < handle->max_depth; i++) { + if (handle->path[i].buf) + ext2fs_free_mem(&handle->path[i].buf); + } + ext2fs_free_mem(&handle->path); + } + ext2fs_free_mem(&handle); +} + + +// ------------------------------------------------------------------------ +// This function inserts the data from block blocknr[0] into the input buffer +// 'buf'. The data from the block is inserted into +// the input buffer beginning at location 'blockcnt'. +// NOTE: The output returned by this command should be corrected to the proper +// endianness for the host cpu when reading multi-byte structures from disk. +int read_block ( ext2_filsys fs, blk_t *blocknr, void *buf ) +{ + errcode_t retval = io_channel_read_blk ( fs->io, *blocknr, 1, buf ); + if (retval) + fprintf(stderr,"Error %d while read block\n", retval); + return retval; +} + + + + + +errcode_t local_ext2fs_extent_open(ext2_filsys fs, struct ext2_inode inode, + ext2_extent_handle_t *ret_handle) { + + struct ext2_extent_handle *handle; + struct ext3_extent_header *eh; + int i; + errcode_t retval; + retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle); + if (retval) + return retval; + memset(handle, 0, sizeof(struct ext2_extent_handle)); + + handle->ino = 0; + handle->fs = fs; + handle->inode = &inode; + + eh = (struct ext3_extent_header *) &handle->inode->i_block[0]; + for (i=0; i < EXT2_N_BLOCKS; i++) + if (handle->inode->i_block[i]) + break; + if (i >= EXT2_N_BLOCKS) { + eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); + eh->eh_depth = 0; + eh->eh_entries = 0; + i = (sizeof(handle->inode->i_block) - sizeof(*eh)) / + sizeof(struct ext3_extent); + eh->eh_max = ext2fs_cpu_to_le16(i); + handle->inode->i_flags |= EXT4_EXTENTS_FL; + } + + + handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth); + handle->type = ext2fs_le16_to_cpu(eh->eh_magic); + + retval = ext2fs_get_mem(((handle->max_depth+1) * + sizeof(struct extent_path)), + &handle->path); + memset(handle->path, 0, + (handle->max_depth+1) * sizeof(struct extent_path)); + handle->path[0].buf = (char *) handle->inode->i_block; + + handle->path[0].left = handle->path[0].entries = + ext2fs_le16_to_cpu(eh->eh_entries); + handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max); + handle->path[0].curr = 0; + handle->path[0].end_blk = + ((((__u64) handle->inode->i_size_high << 32) + + handle->inode->i_size + (fs->blocksize - 1)) + >> EXT2_BLOCK_SIZE_BITS(fs->super)); + handle->path[0].visit_num = 1; + handle->level = 0; + handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE; + + *ret_handle = handle; + //free(handle); + + return 0; +} + + + + +#define check_for_ro_violation_return(ctx, ret) \ + do { \ + if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ + ((ret) & BLOCK_CHANGED)) { \ + (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ + ret |= BLOCK_ABORT | BLOCK_ERROR; \ + return ret; \ + } \ + } while (0) + +#define check_for_ro_violation_goto(ctx, ret, label) \ + do { \ + if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ + ((ret) & BLOCK_CHANGED)) { \ + (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ + ret |= BLOCK_ABORT | BLOCK_ERROR; \ + goto label; \ + } \ + } while (0) + +static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) + ret = (*ctx->func)(ctx->fs, ind_block, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + check_for_ro_violation_return(ctx, ret); + if (!*ind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit; + return ret; + } + if (*ind_block >= ctx->fs->super->s_blocks_count || + *ind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_IND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->ind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { + if (*block_nr == 0) + continue; + flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, + *ind_block, offset, + ctx->priv_data); + changed |= flags; + if (flags & BLOCK_ABORT) { + ret |= BLOCK_ABORT; + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, + ctx->ind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) + ret |= (*ctx->func)(ctx->fs, ind_block, + BLOCK_COUNT_IND, ref_block, + ref_offset, ctx->priv_data); + check_for_ro_violation_return(ctx, ret); + return ret; +} + + + +static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) + ret = (*ctx->func)(ctx->fs, dind_block, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + check_for_ro_violation_return(ctx, ret); + if (!*dind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit; + return ret; + } + if (*dind_block >= ctx->fs->super->s_blocks_count || + *dind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->dind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit; + continue; + } + flags = block_iterate_ind(block_nr, + *dind_block, offset, + ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, + ctx->dind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) + ret |= (*ctx->func)(ctx->fs, dind_block, + BLOCK_COUNT_DIND, ref_block, + ref_offset, ctx->priv_data); + check_for_ro_violation_return(ctx, ret); + return ret; +} + + + +static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, + int ref_offset, struct block_context *ctx) +{ + int ret = 0, changed = 0; + int i, flags, limit, offset; + blk_t *block_nr; + + limit = ctx->fs->blocksize >> 2; + if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | + BLOCK_FLAG_DATA_ONLY))) + ret = (*ctx->func)(ctx->fs, tind_block, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + check_for_ro_violation_return(ctx, ret); + if (!*tind_block || (ret & BLOCK_ABORT)) { + ctx->bcount += limit*limit*limit; + return ret; + } + if (*tind_block >= ctx->fs->super->s_blocks_count || + *tind_block < ctx->fs->super->s_first_data_block) { + ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; + ret |= BLOCK_ERROR; + return ret; + } + ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) { + ret |= BLOCK_ERROR; + return ret; + } + + block_nr = (blk_t *) ctx->tind_buf; + offset = 0; + if (ctx->flags & BLOCK_FLAG_APPEND) { + for (i = 0; i < limit; i++, block_nr++) { + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } else { + for (i = 0; i < limit; i++, block_nr++) { + if (*block_nr == 0) { + ctx->bcount += limit*limit; + continue; + } + flags = block_iterate_dind(block_nr, + *tind_block, + offset, ctx); + changed |= flags; + if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { + ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); + break; + } + offset += sizeof(blk_t); + } + } + check_for_ro_violation_return(ctx, changed); + if (changed & BLOCK_CHANGED) { + ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, + ctx->tind_buf); + if (ctx->errcode) + ret |= BLOCK_ERROR | BLOCK_ABORT; + } + if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && + !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && + !(ret & BLOCK_ABORT)) + ret |= (*ctx->func)(ctx->fs, tind_block, + BLOCK_COUNT_TIND, ref_block, + ref_offset, ctx->priv_data); + check_for_ro_violation_return(ctx, ret); + return ret; +} + + + +errcode_t local_block_iterate3(ext2_filsys fs, + struct ext2_inode inode, // ext2_ino_t ino, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data) +{ + int i; + int r, ret = 0; + // struct ext2_inode inode; + errcode_t retval; + struct block_context ctx; + int limit; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + ctx.errcode = 0; + /*ctx.errcode = ext2fs_read_inode(fs, ino, &inode); + if (ctx.errcode) + return ctx.errcode; + */ + + /* + * Check to see if we need to limit large files + */ + if (flags & BLOCK_FLAG_NO_LARGE) { + if (!LINUX_S_ISDIR(inode.i_mode) && + (inode.i_size_high != 0)) + return EXT2_ET_FILE_TOO_BIG; + } + + limit = fs->blocksize >> 2; + + ctx.fs = fs; + ctx.func = func; + ctx.priv_data = priv_data; + ctx.flags = flags; + ctx.bcount = 0; + if (block_buf) { + ctx.ind_buf = block_buf; + } else { + retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf); + if (retval) + return retval; + } + ctx.dind_buf = ctx.ind_buf + fs->blocksize; + ctx.tind_buf = ctx.dind_buf + fs->blocksize; + + /* + * Iterate over the HURD translator block (if present) + */ + if ((fs->super->s_creator_os == EXT2_OS_HURD) && + !(flags & BLOCK_FLAG_DATA_ONLY)) { + if (inode.osd1.hurd1.h_i_translator) { + ret |= (*ctx.func)(fs, + &inode.osd1.hurd1.h_i_translator, + BLOCK_COUNT_TRANSLATOR, + 0, 0, priv_data); + if (ret & BLOCK_ABORT) + goto abort_exit; + check_for_ro_violation_goto(&ctx, ret, abort_exit); + } + } + + if (inode.i_flags & EXT4_EXTENTS_FL) { + ext2_extent_handle_t handle = NULL; + struct ext2fs_extent extent; + e2_blkcnt_t blockcnt = 0; + blk_t blk, new_blk; + int op = EXT2_EXTENT_ROOT; + int uninit; + unsigned int j; + + ctx.errcode = local_ext2fs_extent_open(fs, inode, &handle); + if (ctx.errcode) + goto abort_exit; + + while (1) { + ctx.errcode = ext2fs_extent_get(handle, op, &extent); + if (ctx.errcode) { + if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT) + break; + ctx.errcode = 0; + if (!(flags & BLOCK_FLAG_APPEND)) + break; + blk = 0; + r = (*ctx.func)(fs, &blk, blockcnt, + 0, 0, priv_data); + ret |= r; + check_for_ro_violation_goto(&ctx, ret, + extent_errout); + if (r & BLOCK_CHANGED) { + ctx.errcode = + ext2fs_extent_set_bmap(handle, + (blk64_t) blockcnt++, + (blk64_t) blk, 0); + if (ctx.errcode || (ret & BLOCK_ABORT)) + break; + continue; + } + break; + } + + op = EXT2_EXTENT_NEXT; + blk = extent.e_pblk; + if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) { + if (ctx.flags & BLOCK_FLAG_DATA_ONLY) + continue; + if ((!(extent.e_flags & + EXT2_EXTENT_FLAGS_SECOND_VISIT) && + !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) || + ((extent.e_flags & + EXT2_EXTENT_FLAGS_SECOND_VISIT) && + (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) { + ret |= (*ctx.func)(fs, &blk, + -1, 0, 0, priv_data); + if (ret & BLOCK_CHANGED) { + extent.e_pblk = blk; + ctx.errcode = + ext2fs_extent_replace(handle, 0, &extent); + if (ctx.errcode) + break; + } + } + continue; + } + uninit = 0; + if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) + uninit = EXT2_EXTENT_SET_BMAP_UNINIT; + for (blockcnt = extent.e_lblk, j = 0; + j < extent.e_len; + blk++, blockcnt++, j++) { + new_blk = blk; + r = (*ctx.func)(fs, &new_blk, blockcnt, + 0, 0, priv_data); + ret |= r; + check_for_ro_violation_goto(&ctx, ret, + extent_errout); + if (r & BLOCK_CHANGED) { + ctx.errcode = + ext2fs_extent_set_bmap(handle, + (blk64_t) blockcnt, + (blk64_t) new_blk, + uninit); + if (ctx.errcode) + goto extent_errout; + } + if (ret & BLOCK_ABORT) + break; + } + } + + extent_errout: + local_ext2fs_extent_free(handle); + ret |= BLOCK_ERROR | BLOCK_ABORT; + goto errout; + } + + /* + * Iterate over normal data blocks + */ + for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { + if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { + ret |= (*ctx.func)(fs, &inode.i_block[i], + ctx.bcount, 0, i, priv_data); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + } + check_for_ro_violation_goto(&ctx, ret, abort_exit); + if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK], + 0, EXT2_IND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit; + if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK], + 0, EXT2_DIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } else + ctx.bcount += limit * limit; + if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { + ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK], + 0, EXT2_TIND_BLOCK, &ctx); + if (ret & BLOCK_ABORT) + goto abort_exit; + } + +abort_exit: + if (ret & BLOCK_CHANGED) { + retval = ext2fs_write_inode(fs, EXT4INO, &inode); + if (retval) { + if (!block_buf) + ext2fs_free_mem(&ctx.ind_buf); + return retval; + } + } +errout: + if (!block_buf) + ext2fs_free_mem(&ctx.ind_buf); + + return (ret & BLOCK_ERROR) ? ctx.errcode : 0; +} +//___________________________________________________________________________________________________ diff --git a/src/block.h b/src/block.h new file mode 100644 index 0000000..1e89608 --- /dev/null +++ b/src/block.h @@ -0,0 +1,26 @@ +#ifndef BLOCK_H +#define BLOCK_H + + +#include <ext2fs/ext2fs.h> + + + + +errcode_t local_block_iterate3(ext2_filsys fs, + struct ext2_inode inode, + int flags, + char *block_buf, + int (*func)(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_blk, + int ref_offset, + void *priv_data), + void *priv_data); + + + + +int read_block ( ext2_filsys, blk_t*, void* ); //read filesystem block +#endif //BLOCK_H diff --git a/src/dir_list.c b/src/dir_list.c new file mode 100644 index 0000000..7f38b15 --- /dev/null +++ b/src/dir_list.c @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +// A special linked list for collect directory entry +// Used for manage old directory blocks in journal + +#include <stdio.h> +#include <stdlib.h> +#include "dir_list.h" + + +// add new Dir-List item +struct dir_list_t* add_list_item (struct dir_list_head_t *head , ext2_ino_t nr, char* name ,char entry){ + struct dir_list_t *this; + if( !nr || (! strcmp(name,""))) + return head->last; + + this = malloc(sizeof(struct dir_list_t)); + if (! this) + goto errout; + this->filename = malloc(strlen(name) + 1); + if (! this->filename) + goto errout; + + head->last->next = this; + head->last = this; + this->next = (struct dir_list_t*) head; + head->count++; + this->entry = entry; + this->inode_nr = nr; + strcpy(this->filename,name); + return (this); + +errout: + fprintf(stderr,"no free memory\n"); + if (this->filename) free(this->filename); + if (this) free(this); + return NULL; +} + + +// destroy Dir-List +int clear_dir_list(struct dir_list_head_t *head) +{ + struct dir_list_t *next; + struct dir_list_t *this; + + next = head->next; + while (next != (struct dir_list_t*) head){ + this = next; + if (next->filename) + free(next->filename); + next = this->next; + free(this); + } + free(head->pathname); + free(head); +return 0; +} + + +// create a new Dir-List +struct dir_list_head_t* new_dir_list (ext2_ino_t path_inode, ext2_ino_t dir_inode, char *path, char *name){ + struct dir_list_head_t *this; + int len_n; + + this = malloc(sizeof(struct dir_list_head_t)); + if (! this) return NULL; + this->next = (struct dir_list_t*) this; + this->last = (struct dir_list_t*) this; + this->path_inode = path_inode; + this->dir_inode = dir_inode; + this->count = 0; + + len_n = strlen(path) + strlen(name) +2; + this->pathname = malloc(len_n); + + if (this->pathname) { + strcpy(this->pathname, path); + if ((strlen(path) > 0) && strcmp(path,"/")) + strcat(this->pathname,"/"); + this->dirname = strchr(this->pathname,0); + strcat(this->pathname , name); + } + else + { + if (this->pathname) free(this->pathname); + free(this); + return NULL; + } + return(this); +} + + +//local helper func ;check for duplicate values +static char d_find_entry(struct dir_list_head_t* dir , ext2_ino_t ino , char *filename){ + char ret = 0; + struct dir_list_t *pointer = dir->next; + while (pointer != (struct dir_list_t*) dir){ + if (strcmp(pointer->filename,filename)) + pointer = pointer->next; + else { + if (pointer->inode_nr == ino) + return 1; + pointer = pointer->next; + } + } +return ret; +} + + +// sort of invalid values by data copy +struct dir_list_head_t* clean_up_dir_list(struct dir_list_head_t* o_dir ){ + struct dir_list_head_t* n_dir; + int i; + char *p; + struct dir_list_t *pointer; + + + n_dir = new_dir_list(o_dir->path_inode,o_dir->dir_inode," "," "); + if (!n_dir) + return o_dir; + +//change the allocatet names + p = n_dir->pathname; + n_dir->pathname = o_dir->pathname; + o_dir->pathname = p; + + p = n_dir->dirname; + n_dir->dirname = o_dir->dirname; + o_dir->dirname = p; + +//copy all practicable values + pointer = o_dir->next; + for (i = o_dir->count ; i>0 ; i-- , pointer = pointer->next ){ + if ( (!pointer->inode_nr) || (pointer->filename[0] == 0) ) + continue; + + switch (pointer->entry){ + case DIRENT_DOT_FILE: +// break; + case DIRENT_DOT_DOT_FILE: + break; + case DIRENT_OTHER_FILE: +// if (d_find_entry(n_dir , pointer->inode_nr , pointer->filename)) +// continue; +// break; + case DIRENT_DELETED_FILE: + if (d_find_entry(n_dir , pointer->inode_nr , pointer->filename)) + continue; + break; + default: + continue; + } + + if(! add_list_item (n_dir, pointer->inode_nr , pointer->filename,0)) + fprintf(stderr, "ERROR by copy of dirlist"); + + } +clear_dir_list(o_dir); +return n_dir; +} + + + + + diff --git a/src/dir_list.h b/src/dir_list.h new file mode 100644 index 0000000..fb9247c --- /dev/null +++ b/src/dir_list.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +// A special linked list for collect directory entry +// Used for manage old directory blocks in journal + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifndef DIR_LIST_H +#define DIR_LIST_H +#include <ext2fs/ext2fs.h> + +//Dir-List item +struct dir_list_t { + struct dir_list_t *next; + char *filename; + ext2_ino_t inode_nr; + char entry; +}; + +//Dir-List header +struct dir_list_head_t { + struct dir_list_t *next; + struct dir_list_t *last; + char *dirname; + char *pathname; + ext2_ino_t path_inode; + ext2_ino_t dir_inode; + int count; +}; + +//function +struct dir_list_t* add_list_item (struct dir_list_head_t*, ext2_ino_t, char* ,char); +int clear_dir_list(struct dir_list_head_t*); +struct dir_list_head_t* new_dir_list ( ext2_ino_t, ext2_ino_t, char*, char*); +struct dir_list_head_t* clean_up_dir_list(struct dir_list_head_t* ); + +#define GET_FIRST(h) ((h)->count) ? (h)->next : NULL +#define GET_NEXT(h,i) ((i)->next != (struct dir_list_t*) (h)) ? (i)->next : NULL + + +#endif diff --git a/src/ext2fsP.h b/src/ext2fsP.h new file mode 100644 index 0000000..1841e16 --- /dev/null +++ b/src/ext2fsP.h @@ -0,0 +1,87 @@ +/* + * ext2fsP.h --- private header file for ext2 library + * + * Copyright (C) 1997 Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + */ + +#include <ext2fs/ext2fs.h> + +/* + * Badblocks list + */ +struct ext2_struct_u32_list { + int magic; + int num; + int size; + __u32 *list; + int badblocks_flags; +}; + +struct ext2_struct_u32_iterate { + int magic; + ext2_u32_list bb; + int ptr; +}; + + +/* + * Directory block iterator definition + */ +struct ext2_struct_dblist { + int magic; + ext2_filsys fs; + ext2_ino_t size; + ext2_ino_t count; + int sorted; + struct ext2_db_entry * list; +}; + +/* + * For directory iterators + */ +struct dir_context { + ext2_ino_t dir; + int flags; + char *buf; + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data); + void *priv_data; + errcode_t errcode; +}; + +/* + * Inode cache structure + */ +struct ext2_inode_cache { + void * buffer; + blk_t buffer_blk; + int cache_last; + int cache_size; + int refcount; + struct ext2_inode_cache_ent *cache; +}; + +struct ext2_inode_cache_ent { + ext2_ino_t ino; + struct ext2_inode inode; +}; + +/* Function prototypes */ +extern int ext2fs_process_dir_block(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block, + int ref_offset, + void *priv_data); + + diff --git a/src/ext4magic.8 b/src/ext4magic.8 new file mode 100644 index 0000000..efbc3bd --- /dev/null +++ b/src/ext4magic.8 @@ -0,0 +1,512 @@ +.TH extmagic 8 "May 2010" "version 0.1.2" "Administration Tool" +.SH NAME +ext4magic \- allows to recover deleted files on ext3/4 filesystems +.SH SYNOPSIS +.B ext4magic +[\-S|\-J|\-H|\-V|\-T] [\-x] [\-j <journal_file>] [\-B n|\-I n|\-f <file_name>|\-i <input_list>] [\-t n|[[\-a n][\-p n]]] [\-d <target_dir>] [\-R|\-r|\-L|\-l] [\-Q] <filesystem> + + +.SH DESCRIPTION +The deletion of files in ext3/4 filesystems can not be easily reversed. +Zero out of the block references in the Inodes makes that impossible. +Experience with other programs have proved, it is often possible, to +restore sufficient information for a recover of many data files, directly from the +filesystem Journal. ext4magic can extract the information from the +Journal, and can restore files in entire directory trees, provided that +the information in the Journal are sufficient. This tool can recover +the most file types, can recover large and sparse files, +recovered files with orginal filename, with the orginal owner an +group, the orginal file mode bits, and also the old atime/ctime stamp. + +The filesystem Journal has a very different purpose, and it will not +be possible to recover any file at any time. Many factors affect which data and how +long the data store in the Journal. Read the ext4magic documentation for more +extensive information about the filesytem Journal. + +.B +Direct use of the Journal of a currently read-write open filesystem produce reading of bad blocks. Such bad blocks provide program errors and false results. You shall therefore never use the Journal of such a read-write open file system directly. + + + +.SH OPTIONS +.B +Information Options: +These options generate generic status information from the filesystem and the Journal. + +.TP +.B +\-S +Print the filesystem superblock, the option. +.B +\-x +allows the additional display of content of the group descriptor table. +.TP +.B +\-J +Print the content of the Journal superblock. +This option also can used to force loading the Journal. This has a flow control effect in ext4magic with some other options. +.TP +.B +\-H +Output a histogram of time stamps from all filesystem Inodes. Allows you to determine the exact time of changes in the filesystem. In connection with a directory name or a directory Inode, only the time stamps of this directory tree will be displayed. There are not evaluated any changes, only one per Inode. either the last change or the deletion time per Inode arrives to display. If present (ext4), it also create a histogram +of create time stamps. + +The optional option +.B +\-x +allows additional a better resolution of the time intervals. + +.TP +.B +\-V +Print the version of ext4magic and libext2fs + +.TP +.B +\-T +Display the entire transaction list of all copies of data blocks in the Journal. In conjunction with the +.B +\-B ; \-I +and +.B +\-f +, only display the corresponding data blocks for this data . The optional option +.B +\-x +allows a additional transmission time of the transactions, but only if the block is a Inode block. The print is in the same order as the data in journal. You can make conclusions from the data received in the Journal. +After the import of backups or after change the atime and ctime of files, the additional transmission time will display not always the real transmission time. +If here absolutely incorrect time entries, then check if you using a journal of a read-write open file system. + +.TP +.B +\-x +controls optional the output format and the information content of certain commands. Affects the following options: +.B +\-S ; \-H ; \-T ; \-B ; \-I ; \-f ; \-L ; \-l +Detailed description see there. + + +.PP +.B +Selection Options: +These options specify the exact files, directories, and data blocks. One hand, they produce specific information, and on the other hand, +be used to address the data for the +.B +Action Options. +ext4magic will accept only one of these options at command. +.TP +.B +\-B n +.B +n +is the data block number of a filesystem datablock. Without further options it print a "one-byte" hex+ASCII dump from the data block on the filesystem, like the +.B +"hexdump \-C" +command. The optional option +.B +\-x +produced a "four byte" hex+ASCII output. + +With the option +.B +\-t n +it print a copy of the filesystem data block with this transaction number from the Journal. + +.B +# ext4magic /dir/filesytem.iso \-B 97 \-t 22 + +print a hexdump of the copy from filesystem block number 97, which has been writing to the Journal with the transaction number 22. All copies of a particular data block in the Journal and the associated transaction numbers you can find with the optional Option +.B +\-T + +.B +# ext4magic /dir/filesystem.iso \-B 97 \-T + +will print a list with all copies of filesystem block number 97 with the transaction numbers. If this data block is a Inode block, print out the exact time for the transaction with the optional option +.B +\-x + + +.TP +.B +\-I n +.B +n +is the Inode number. Without any other option, the output is the content of the real filesystem Inode. With a optional +.B +\-x +additional output of a list of all data blocks addressed by this Inode. If Inode is a directory Inode, the content of the directory entrys also printed. + + +Together with one of the following option +.B +\-T ; \-J +the output is not the content from the real filesystem Inode. The content of all differend Inode copies found in the Journal are printed. + + +with the option +.B +\-t n +only the content of the Inode from transaction " +.B +n +" are printed. + + +the option +.B +\-I n +can also be used in conjunction with the options +.B +\-L ; \-l ; \-r +or +.B +\-R +(show there) + + + +.TP +.B +\-f <filename> +the function is the same as +.B +\-I n +only here is the +.B +<filename> +given instead the Inode number. ext4magic search the filesystem to find the Inode number. +The filename can be a directory or a filename und must be specified here from the root directory of this filesystem, and not from the root directory of the LINUX system. + +An example: +the mount point for this filesystem is " +.B +/home +" an the filename for Linux is " +.B +/home/usr1/Document +" you can use now +.B + # extmagic /dev/sda3 -f usr1/Document + +The root directory of the filesystem you can use + +.B +-f / + or + +.B +-f "" + for ext4magic this is the same. + +you should specify no leading "/" for all other filename. And directory names you should specify without final "/" . + + +.PP +.B +Time Options: +With this options you specify the time at which the program searches for matching time stamps in the Journal data. +ext4magic required for most internaly functions two times. A time "after" and a time "before". + +Found Inode only accepted, if not deleted and there time stamp less than "before". If the delete time is less then "after", the Inode are also not used. ext4magic is still trying to find for valid directory Inode also a time-matching directory data. For a recover action "before" must always set to a value at which the data deleted, and +"after" must set to a value at which the data available. Inodes and directory data with other timestamps will be skipped and not used. + +Default, without any time option, ext4magic will search with "now" for the internal time "before", and +"now -24 hour" for the internal time "after". If you try to recover without any time option, so you search only over the last 24 hours. If you wait a couple of days before you try to recover deleted data, you must always use time options, or you find nothing + +.TP +.B +\-a n +with this option you can set the " +.B +after +" time +.TP +.B +\-p n +with this option you can set the " +.B +before +" time + +.B +n +is the number of seconds since 1970-01-01 00:00:00 UTC. This time information can you find in many prints of ext4magic, and you can it produce on the console with the command "date" and also insert directly in the ext4magic command line. + +.B +-a $(date -d "-3day" +%s) -p $(date -d "-2day" +%s) + +this example set "after=now-36h" and "before=now-24h" + +.TP +.B +\-t n +is an indirect time option. you can use it with the options +.B +\-B ; \-I ; \-f +The value +.B +n +is the transaction number. With this option you can print, list, or recover the data from this transaction number. +you can find the transaction numbers with the option +.B +\-T +or in the print of the Inode content. + + +.PP +.B +File-, IN- and OUT-Options: +With these options group, you select the filesystem, and other optional file input and output for control of ext4magic. +.TP +.B +\<filesystem> +selects the filesystem and must always be set. +.B +<filesystem> +can be a blockdevice with ext3/4 filesystem, it can also be a uncompressed file image of such a partition. + + +.TP +.B +\-j <journal_file> +optional you can select a external copy of the Journal file. Without this option, automatically the internal Journal or, if configured, the external Journal on a block device will used. + + +.TP +.B +\-d <target_dir> +select the output directory. There, the recovered files were written. If it does not exist, it is created. By default, created files are written to the subdirectory " +.B +RECOVERDIR +" in the workpath of the actual shell. This output directory can not be on the same filesystem to be tested filesytem, and should have sufficient space to write the recovered files. The filesystem on this directory should be also ext3/4, otherwise, not LINUX like filesytems generate some errors while writing the file properties. +Either you must first changed with the shell in such a suitable filesystem, or you must specify the +.B +\-d +with a target to such a directory + + +.TP +.B +\-i <input_list> +input_list is a input file. Must contain a list with double-quoted filenames. The files from the list will be restored with option +.B +\-r +or +.B +\-R + +Blank lines, not cleanly double quoted filenames and all areas before and after +.B +" +will be ignored. +Such a double-quoted list of file names can create with options +.B +\-l \-x +or +.B +\-L \-x +by ext4magic and edited by script or by hand. + + + +.PP +.B +Action Options: +This option group includes list and recover options. All functions together, they work recursiv controlled by the time options through directory trees. The starting point for search is determined by a directory name or a directory Inode number. Default is root of this Filesystem. Matching the time options, the filesystem data, inclusive directory data, taken from the Journal. If good data from the file system sections available in Journal, it is possible to see or recover the state of the filesystem at different times. + + +.TP +.B +\-L +Prints the list of all filenames and Inode number of the selected directory tree. Included here also are deleted files and deleted directory trees. +With the additional option. +.B +\-x +the file names are printed double-quoted. You can use it for a "Input list" with option +.B +\-i + + +.TP +.B +\-l +Prints a list of all filenames which have not allocated data blocks. At the beginning of the line are the percentage of unallocated data blocks. +After deletion you find here all the file names you can recover with the Journal data. If you use a very old value for the "before" time, it is possible there are files whose data blocks reused and these files in the interim also been deleted. Also included in the list all files without data blocks, symbolic links, empty and other special files. + +Likewise double-quoted file names with optional +.B +\-x + + +.TP +.B +\-r +applied to directories, all files without conflicts with the occupied blocks will recovered. This are all you can sea with the option +.B +\-l +and be 100% unallocated. This options only recover deleted files and files without data blocks, in example: symbolic links or empty files. + +The recovered files written to the +.B +RECOVERDIR/ +This can also set to an alternate <target_dir> with the option +.B +\-d + +All files become the old filename and if possible, also the old file properties. A subdirectory tree can set with +.B +"\-f dirname" +oder +.B +"\-I inodenumber" +If use with a given Inode number, the directory name is set to +.B +<inodenumber> + +The Time options affect the search. If a file name already exists, or you recover again, it not overwrite files, and a new filename by added a final +.B +"#" +will created. The maximum ist the extension " +.B +###### +" for a filename. + +single files also can recovered, possible search with time-stamps or transaction number. + + +.TP +.B +\-R +recovers directory tree, is the same as +.B +\-r + +But two very important differences: +Recover of all matched Inodes, even if the blocks allocated, +and recover if possible the old directory properties. Also empty dirctories will be restored. +This recovers all deleted and all undeleted files, and it's possible to recover older file versions or directory versions. + +In completely deleted directories the behavior " +.B +-R +" and " +.B +-r +" is identical. The difference is there only the complete recover of all directories with option " +.B +-R +". +You can also restore individual files with time options or a transaction number. + + +.TP +.B +\-Q +This is a optional high quality Option for recover and only impact with " +.B +-r +" and " +.B +-R +". Without this option, any valid file name restored from the directories and you can set the " +.B +before +" time stamp to a time in which all files are deleted. So you will find the maximum possible number of files. +It need not necessarily be found old directory data blocks in the Journal. +However, there are some files found too much. In this mode, re-used file name and reused Inode can not be noticed. As a result some file will be created with the extension " +.B +"#" +or some files created with wrong content. You have to check the files and find bad files and delete itself. + +With option " +.B +-Q +" works ext4magic more accurately, and can avoid such false and duplicate files. This requires old data blocks of the directories in the Journal. You will not find of all directories those old blocks in the Journal. Only directories in which files have been previously created or deleted, but not of directories in which no change has been a long time. You should set the time stamp " +.B +before +" immediately before destruction time of the files. Are not sufficient directory data available, may be, ext4magic can't found deleted files or entire directory content. + +.PP +.B +For all recover cases +ACL and extended attribute can not recovered in the current version. + +The output starts at line with a string "--------" before the recovered file name. This is a sign of successful recover. Are not enough permissions to write the recovered files, then you will see there some "x" in the string. + +At the end of the process, possibly an issue comes from the hardlink database. A positive number before a file name means : not found all hardlinks to this file. A negative number means : it created too many hardlinks to this file (possible are, reused filenames or reused Inodes, and so, too many or wrong old filenames for this hardlink. - But also possible - all files for this hardlink are correct, the time-options was not set correct and because of that, the selected inode for the recover was not up to date. You should check such reports.) + +Re-used data blocks can't realize and so it's possible, it ends in some corrupted files. +Check in any case, all the recoverd files before you use them. + + + +.SH EXAMPLES +.TP +Print the content of a Inode, there are some possibilities. + +.B + # ext4magic /dev/sda3 -f / + +.B + # ext4magic /dev/sda3 -I 2 + +the output is the actual filesystem Inode. In first example input the pathname, second example Inode 2 is also the root directory + + + +.B + # ext4magic /tmp/filesystem.iso -f / -T -x + +use filesystem image "/tmp/filesystem.iso", search and print all transactions of the Block which included the root Inode, and print all differend +Inode. Inclusiv the blocklist off the data blocks. If it's a directory, then print also for each individual Inode the content of the directory. + + + +.B + # ext4magic /tmp/filesystem.iso -j /tmp/journal.backup -I 8195 -t 182 + +Use filesystem image "/tmp/filesystem.iso" and read from external Journal in file "/tmp/journal.backup" and +print the content of the Inode number 8195 from the journal transaction number 182 + + + +.B + # ext4magic /dev/sda3 -f user1/Documents -a $(date -d "-3 day" +%s) -p $(date -d "-2 day" +%s) + +print a undeleded Inode for pathname "user1/Documents" two to three days back. If it's a directory, then also the content of this directory. +If can not found the old directory blocks in Journal, the directory content would be the actual from filesystem. + + +.TP +Examples of simple Recover + +.B + # ext4magic /dev/sda3 -r + +try to restore all files deleted last 24 hours. Write in directory "./RECOVERDIR/" + + +.B + # ext4magic /dev/sda3 -RQ -f user1/Dokuments -a 1274210280 -p 1274211280 -d /mnt/testrecover + +try to restore the directory tree "user1/Dokuments/". The "-p" timestamp you mast set just before deleting files, the "-a" timestamp prevents found old file versions. This will only work well, if you've there created or deleted files bevor the "-p" timestamp. Write in directory "/mnt/testrecover/" + + +.B + # ext4magic /home/filesystem.iso -Lx -f user1 | grep "jpg" > ./tmpfile +.B + # ext4magic /home/filesystem.iso -i ./tmpfile -r -d /mnt/testrecover + +try to restore only all deleted files from directory tree "user1/", and have "jpg" in filename. (last 24 hour) and write to "/mnt/testrecover" - use a temporary file "./tmpfile" for a list of filenames. + + +.SH AUTHOR +Roberto Maar + +.SH SEE ALSO +debugfs(8) + + + + diff --git a/src/ext4magic.c b/src/ext4magic.c new file mode 100644 index 0000000..b356e13 --- /dev/null +++ b/src/ext4magic.c @@ -0,0 +1,900 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +extern int optind; +extern char *optarg; +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +/* ext3/4 libraries */ +#include <ext2fs/ext2fs.h> + +//local header files +#include "util.h" +#include "ext4magic.h" +#include "journal.h" +#include "inode.h" +#include "hard_link_stack.h" + + + +ext2_filsys current_fs = NULL; +ext2_ino_t root, cwd; + + + +//print Versions an CPU-endian-type +void print_version ( void ) +{ + printf("ext4magic version : %s\n",(char*) &VERSION); + + const char *libver; + ext2fs_get_library_version ( &libver, NULL ); + printf("libext2fs version : %s\n",libver); + + int n = 1 ; + if ( * ( char * ) &n == 1 ) // True if the cpu is little endian. + printf("CPU is little endian.\n"); + else + printf("CPU is big endian.\n"); +} + + +//subfunction for show_super_stats() +static void print_bg_opts(struct ext2_group_desc *gdp, int mask, + const char *str, int *first, FILE *f) +{ + if (gdp->bg_flags & mask) { + if (*first) { + fputs(" [", f); + *first = 0; + } else + fputs(", ", f); + fputs(str, f); + } +} + +//print superblock +void show_super_stats(int header_only) +{ + dgrp_t i; + FILE *out; + struct ext2_group_desc *gdp; + int c; + int numdirs = 0, first, gdt_csum; + + out=stdout; + + list_super2(current_fs->super, out); + if (! header_only) { + return; + } + + for (i=0; i < current_fs->group_desc_count; i++) + numdirs += current_fs->group_desc[i].bg_used_dirs_count; + fprintf(out, "Directories: %d\n", numdirs); + + + gdt_csum = EXT2_HAS_RO_COMPAT_FEATURE(current_fs->super, + EXT4_FEATURE_RO_COMPAT_GDT_CSUM); + gdp = ¤t_fs->group_desc[0]; + for (i = 0; i < current_fs->group_desc_count; i++, gdp++) { + fprintf(out, " Group %2d: block bitmap at %u, " + "inode bitmap at %u, " + "inode table at %u\n" + " %d free %s, " + "%d free %s, " + "%d used %s%s", + i, gdp->bg_block_bitmap, + gdp->bg_inode_bitmap, gdp->bg_inode_table, + gdp->bg_free_blocks_count, + gdp->bg_free_blocks_count != 1 ? "blocks" : "block", + gdp->bg_free_inodes_count, + gdp->bg_free_inodes_count != 1 ? "inodes" : "inode", + gdp->bg_used_dirs_count, + gdp->bg_used_dirs_count != 1 ? "directories" + : "directory", gdt_csum ? ", " : "\n"); + if (gdt_csum) + fprintf(out, "%d unused %s\n", + gdp->bg_itable_unused, + gdp->bg_itable_unused != 1 ? "inodes":"inode"); + first = 1; + print_bg_opts(gdp, EXT2_BG_INODE_UNINIT, "Inode not init", + &first, out); + print_bg_opts(gdp, EXT2_BG_BLOCK_UNINIT, "Block not init", + &first, out); + if (gdt_csum) { + fprintf(out, "%sChecksum 0x%04x", + first ? " [":", ", gdp->bg_checksum); + first = 0; + } + if (!first) + fputs("]\n", out); + } + return; +} + + + +//open and init the Filesystem, use in main() +static void open_filesystem(char *device, int open_flags, blk_t superblock, + blk_t blocksize, int catastrophic, + char *data_filename) +{ + int retval; + io_channel data_io = 0; + + if (superblock != 0 && blocksize == 0) { + fprintf(stderr,"if you specify the superblock, you must also specify the block size\n"); + current_fs = NULL; + return; + } + + if (data_filename) { + if ((open_flags & EXT2_FLAG_IMAGE_FILE) == 0) { + fprintf(stderr,"The -d option is only valid when reading an e2image file\n"); + current_fs = NULL; + return; + } + retval = unix_io_manager->open(data_filename, 0, &data_io); + if (retval) { + fprintf(stderr,"%s while opening data source\n" ,data_filename); + current_fs = NULL; + return; + } + } + + if (catastrophic && (open_flags & EXT2_FLAG_RW)) { + fprintf(stderr,"opening read-only because of catastrophic mode\n"); + open_flags &= ~EXT2_FLAG_RW; + } + + retval = ext2fs_open(device, open_flags, superblock, blocksize, + unix_io_manager, ¤t_fs); + if (retval) { + fprintf(stderr,"%s %d while opening filesystem \n", device, retval); + current_fs = NULL; + return; + } + + + if (catastrophic) + fprintf(stderr,"%s catastrophic mode - not reading inode or group bitmaps\n", device); + else { + retval = ext2fs_read_inode_bitmap(current_fs); + if (retval) { + fprintf(stderr,"%s %d while reading inode bitmap\n", device, retval); + goto errout; + } + retval = ext2fs_read_block_bitmap(current_fs); + if (retval) { + fprintf(stderr,"%s %d while reading block bitmap\n",device, retval); + goto errout; + } + } + + if (data_io) { + retval = ext2fs_set_data_io(current_fs, data_io); + if (retval) { + fprintf(stderr,"%s %d while setting data source\n", device, retval); + goto errout; + } + } + + root = cwd = EXT2_ROOT_INO; + return; + +errout: + retval = ext2fs_close(current_fs); + if (retval) + fprintf(stderr, "%s %d while trying to close filesystem\n", device, retval); + current_fs = NULL; +} + + +//subfunction for main +void print_modus_error(){ + char message0[] = "Warning: only input of one modus allowed [ -R | -r | -L | -l | -H ]\n"; + fprintf(stderr,"%s",message0); +} + + +//------------------------------------------------------------------------------------------------------------- +// the main() function +int main(int argc, char *argv[]){ +char* progname = argv[0]; +int retval, exitval; +char defaultdir[] = "RECOVERDIR" ; +//FIXME : usage is not correct +const char *usage = "[-S|-J|-H|-V|-T] [-x] [-j <journal_file>] [-B n|-I n|-f <file_name>|-i <input_list>] [-t n|[[-a n][-p n]]] [-d <target_dir>] [-R|-r|-L|-l] [-Q] <filesystem>"; +int c; +int open_flags = EXT2_FLAG_SOFTSUPP_FEATURES; +int exit_status = 0 ; +int recovermodus = 0 ; +int recoverquality = DELETED_OPT; // default use also delete dir entry +char *j_file_name = NULL; +char *pathname = NULL; +char *des_dir = NULL; +char *input_filename = NULL; +blk_t superblock = 0; +blk_t blocksize = 0; +int transaction_nr = 0; +//int catastrophic = 0; +char *data_filename = 0; +int mode = 0; +ext2_ino_t inode_nr = EXT2_ROOT_INO ; +blk_t block_nr = 0; +int format = 0; +int journal_flag = JOURNAL_CLOSE ; +int journal_backup = 0; +__u32 t_before = 0; +__u32 t_after = 0; +struct stat filestat; + + + // Sanity checks on the user. +if ( argc < 3 ) + { + printf("%s : Error: Missing device name and options.\n", progname); + printf("%s \n", usage); + return EXIT_FAILURE; + } + + { +// set default Time from "now -1 day" to "now" + time_t help_time; + time( &help_time ); + t_before = (__u32) help_time; + t_after = t_before - 86400 ; + } + +// decode arguments +while ((c = getopt (argc, argv, "TJRLlrQSxi:t:j:f:Vd:B:p:a:I:H")) != EOF) { + switch (c) { + case 'S': + mode |= PRINT_SUPERBLOCK ; + break; + + case 'H': + if(mode & RECOVER_INODE){ + print_modus_error(); + break; + } + mode |= RECOVER_INODE; + mode |= PRINT_HISTOGRAM ; + break; + + case 'R': + if(mode & RECOVER_INODE){ + print_modus_error(); + break; + } + mode |= RECOVER_INODE; + mode |= READ_JOURNAL; + recovermodus = RECOV_ALL ; + break; + + case 'r': + if(mode & RECOVER_INODE){ + print_modus_error(); + break; + } + mode |= RECOVER_INODE; + mode |= READ_JOURNAL; + recovermodus = RECOV_DEL ; + break; + + case 'L': + if(mode & RECOVER_INODE){ + print_modus_error(); + break; + } + mode |= RECOVER_INODE; + mode |= READ_JOURNAL; + recovermodus = LIST_ALL ; + break; + + case 'l': + if(mode & RECOVER_INODE){ + print_modus_error(); + break; + } + mode |= RECOVER_INODE; + mode |= READ_JOURNAL; + recovermodus = LIST_STATUS ; + break; + + case 'd': + des_dir = optarg; + //we check later + break; + + case 'i': + mode |= RECOVER_LIST; + mode |= READ_JOURNAL; + input_filename = optarg; + retval = stat (input_filename, &filestat); + if (retval){ + fprintf(stderr,"Error: Invalid parameter: -i %s\n", optarg); + fprintf(stderr,"filestat %s returns error %d\n", input_filename, retval); + exitval = EXIT_FAILURE ; + goto errout; + } + else{ + if (S_ISREG(filestat.st_mode) && (! access(input_filename,R_OK))){ + printf("\"%s\" accept for inputfile \n",input_filename); + } + else { + fprintf(stderr,"ERROR: can not use \"%s\" for inputfile\n",input_filename); + exitval = EXIT_FAILURE ; + goto errout; + } + } + break; + + case 'I': + if (mode & COMMAND_INODE){ + fprintf(stderr,"Warning: only input of one inodeNR or filename allowed\n"); + break; + } + mode |= COMMAND_INODE ; + errno = 0; + inode_nr = strtol ( optarg, NULL, 10 ); + if ( errno ) + { + fprintf(stderr,"Error: Invalid parameter: -I %s \n", optarg ); + exitval = EXIT_FAILURE ; + goto errout; + } + if ( inode_nr < 1 ) + { + fprintf(stderr,"Error: %s -I: inodeNR \n", progname); + fprintf(stderr,"%d is out of range\n", inode_nr); + exitval = EXIT_FAILURE ; + goto errout; + } + break; + + case 'B': + mode |= COMMAND_BLOCK ; + errno = 0; + block_nr = strtol ( optarg, NULL, 10 ); + if ( errno ) + { + fprintf(stderr,"Error: Invalid parameter: -B %s \n", optarg ); + exitval = EXIT_FAILURE ; + goto errout; + } + if ( block_nr < 1 ) + { + fprintf(stderr,"Error: %s -B: blockNR \n", progname); + fprintf(stderr,"%d is out of range\n", block_nr); + exitval = EXIT_FAILURE ; + goto errout; + } + break; + + case 't': + mode |= PRINT_TRANSACTION ; + mode |= READ_JOURNAL; + errno = 0; + transaction_nr = strtol ( optarg, NULL, 10 ); + if ( errno ) + { + fprintf(stderr,"Error: Invalid parameter: -t %s \n", optarg ); + exitval = EXIT_FAILURE ; + goto errout; + } + if ( transaction_nr < 1 ) + { + fprintf(stderr,"Error: %s -t: transactionNR \n", progname); + fprintf(stderr,"%d is out of range\n", transaction_nr); + exitval = EXIT_FAILURE ; + goto errout; + } + break; + + case 'x': + format = 1; + break; + + case 'Q': + mode |= HIGH_QUALITY; + break; + + case 'w'://experimental not activ at default + journal_backup=1; + break; + + case 'j': + j_file_name = optarg; + retval = stat (j_file_name, &filestat); + if (retval){ + fprintf(stderr,"Error: Invalid parameter: -j %s\n", optarg); + fprintf(stderr,"filestat %s returns error %d\n", j_file_name, retval); + exitval = EXIT_FAILURE ; + goto errout; + } + break; + + case 'f': + if (mode & COMMAND_INODE){ + fprintf(stderr,"Warning: only input of one inodeNR or filename allowed\n"); + break; + } + mode |= COMMAND_INODE ; + mode |= COMMAND_PATHNAME ; + pathname = malloc(512); + strcpy(pathname,optarg); + break; + + case 'b'://experimental not activ at default + blocksize = parse_ulong(optarg, argv[0], + "block size", 0); + break; + case 's'://experimental not activ at default + superblock = parse_ulong(optarg, argv[0], + "superblock number", 0); + break; + case 'T': + mode |= PRINT_BLOCKLIST; + mode |= READ_JOURNAL; + break; + + case 'J': + mode |= PRINT_J_SUPERBLOCK; + mode |= READ_JOURNAL; + break; + + case 'p': + errno = 0; + t_before = strtol ( optarg, NULL, 10 ); + if ( errno ) + { + fprintf(stderr,"Error: Invalid parameter: -p %s \n", optarg ); + exitval = EXIT_FAILURE ; + goto errout; + } + if ( t_before < 1 ) + { + fprintf(stderr,"Error: %s -p: time \n", progname); + fprintf(stderr,"%d is out of range\n", inode_nr); + exitval = EXIT_FAILURE ; + goto errout; + } + mode |= INPUT_TIME; + break; + + case 'a': + errno = 0; + t_after = strtol ( optarg, NULL, 10 ); + if ( errno ) + { + fprintf(stderr,"Error: Invalid parameter: -a %s \n", optarg ); + exitval = EXIT_FAILURE ; + goto errout; + } + if ( t_after < 1 ) + { + fprintf(stderr,"Error: %s -a: time \n", progname); + fprintf(stderr,"%d is out of range\n", inode_nr); + exitval = EXIT_FAILURE ; + goto errout; + } + mode |= INPUT_TIME; + break; + + + case 'z': //experimental not activ at default + data_filename = optarg; + break; + + case 'y': //experimental not activ at default + open_flags |= EXT2_FLAG_IMAGE_FILE; + break; + + case 'V': + print_version(); + exit(0); + + default: + fprintf(stderr,"Usage: %s %s\n",argv[0], usage); + exitval = EXIT_FAILURE ; + goto errout; + } + } + + if (getuid()) mode = 0; + if (optind < argc) + open_filesystem(argv[optind], open_flags,superblock, blocksize, 0, data_filename); + + +//-------------------------------------------------------------------------------------------- +// check any parameter an options +// check time option +if (mode & INPUT_TIME){ + if (! ((t_after > 315601200) && (t_after < t_before))) // 315601200 = "1980-01-01 20:00:00" + { + fprintf(stderr,"Invalide parameter: range \"AFTER <--> BEFORE\"\n"); + fprintf(stderr,"the automatic default parameter AFTER=\"now -1 day\" ; BEFORE=\"now\"\n"); + fprintf(stderr,"\"-p before-timestamp\" must greater then \"-a after-timestamp\"\n"); + fprintf(stderr,"Example : %s -H -p $(date +%%s) -a $(date -d \"-1 day\" +%%s) %s\n",progname,current_fs->device_name); + exitval = EXIT_FAILURE ; + goto errout; + } + if (mode & PRINT_TRANSACTION){ + fprintf(stderr,"Invalide parameter: use either Transaction-Nr or Timestamps for search in Journal\n"); + exitval = EXIT_FAILURE ; + goto errout; + } + mode |= READ_JOURNAL; +} + + +//check for the recoverdir +if ((mode & RECOVER_INODE) && (recovermodus & (REC_DIR_NEEDED)) || mode & RECOVER_LIST) { + if(!des_dir) + des_dir = defaultdir; + + retval = stat (des_dir, &filestat); + if (retval){ + retval = mkdir(des_dir , S_IRWXU); + if (retval && (errno != EEXIST)){ + fprintf(stderr,"ERROR: can not create the recover directory: %s\n", des_dir); + exitval = EXIT_FAILURE ; + goto errout; + } + else + retval = stat (des_dir, &filestat); + } + if (!retval){ + if (S_ISDIR(filestat.st_mode) && (filestat.st_mode & S_IRWXU) == S_IRWXU){ + printf("\"%s\" accept for recoverdir \n",des_dir); + } + else { + fprintf(stderr,"ERROR: can not use \"%s\" for recover directory\n", des_dir); + exitval = EXIT_FAILURE ; + goto errout; + } + } + else{ + fprintf(stderr,"Error: Invalid parameter: -d %s \n", des_dir ); + exitval = EXIT_FAILURE ; + goto errout; + } +// set quality + if (mode & HIGH_QUALITY) + recoverquality = 0 ; // not use of delete dir entry + +} + +//mark for douple quotes filename +if ((recovermodus & (LIST_ALL | LIST_STATUS)) && format) + recovermodus |= DOUPLE_QUOTES_LIST; + + +//................................................................................................ +// begin Operation + if (current_fs) { + // printf( "Open mode: read-%s\n", current_fs->flags & EXT2_FLAG_RW ? "write" : "only"); + printf("Filesystem in use: %s\n\n",current_fs ? current_fs->device_name : "--none--"); + init_link_stack(); + + +// print filesystem superblock + if ((mode & PRINT_SUPERBLOCK) && (! ( mode & READ_JOURNAL))) show_super_stats(format); + + +// print histogram over inodeblock + if (mode & PRINT_HISTOGRAM){ + if (mode & COMMAND_INODE){ + mode |= READ_JOURNAL; + recovermodus = HIST_DIR ; + } + else { + mode = 0 ; + read_all_inode_time(current_fs,t_after,t_before,format); + } + } + + +// no file or directory is given, use the default root-inode at now and set COMMAND_INODE flag + if((mode & RECOVER_INODE) && (! (mode & COMMAND_INODE))) + mode |= COMMAND_INODE ; + + + +// print filesystem inode + if ((mode & COMMAND_INODE) && (! (mode & READ_JOURNAL))) + { + int allocated; + retval = 0; + struct ext2_inode *inode_buf; + + // search for inode if the pathname is given + if (mode & COMMAND_PATHNAME){ + if(! strcmp(pathname,"/") || ! strcmp(pathname,"")){ + inode_nr = EXT2_ROOT_INO ; + *pathname = 0; + } + else{ + int l=strlen(pathname) -1; + if (*(pathname + l) == '/') + *(pathname + l) = 0 ; + retval = ext2fs_namei(current_fs,EXT2_ROOT_INO ,EXT2_ROOT_INO , pathname, &inode_nr); + } + if (retval) { + fprintf(stderr,"Error: Filename \"%s\" not found in Filesystem\n", pathname); + fprintf(stderr,"if \"%s\" deleted, use InodeNr or try Journaling options\n",pathname); + exitval = EXIT_FAILURE ; + goto errout; + } + } + + inode_buf = malloc(EXT2_INODE_SIZE(current_fs->super)); + if (intern_read_inode_full(inode_nr, inode_buf,EXT2_INODE_SIZE(current_fs->super))) return; + + allocated = ext2fs_test_inode_bitmap ( current_fs->inode_map, inode_nr ); + + fprintf(stdout,"\nDump internal Inode %d\nStatus : %s\n\n",inode_nr,(allocated) ? "Inode is Allocated" : "Inode is Unallocated"); + dump_inode(stdout, "",inode_nr, inode_buf, format); + if (LINUX_S_ISDIR(inode_buf->i_mode)) list_dir(inode_nr); + free(inode_buf); + } + + +// print filesystem block + if ((mode & COMMAND_BLOCK) && (! (mode & READ_JOURNAL))) + { + void *block_buf; + int allocated; + block_buf = malloc(EXT2_BLOCK_SIZE(current_fs->super )); + read_block ( current_fs , &block_nr , block_buf ); + allocated = ext2fs_test_block_bitmap ( current_fs->block_map, block_nr ); + fprintf(stdout,"Dump Filesystemblock %10lu Status : %s\n",block_nr,(allocated) ? "Block is Allocated" : "Block is Unallocated"); + blockhex ( stdout , block_buf , format , EXT2_BLOCK_SIZE(current_fs->super )); + free(block_buf); + } + + + +//----------------------------------------------------- +// loading Journal +if (mode & READ_JOURNAL){ + if (journal_flag) journal_flag = journal_open( j_file_name , journal_backup ); + if (journal_flag != JOURNAL_OPEN){ + exitval = EXIT_FAILURE ; + goto errout; + } + + +//print the after and before Time + if (mode & INPUT_TIME){ + printf("Activ Time after : %s", time_to_string(t_after)); + printf("Activ Time before : %s", time_to_string(t_before)); + } + + +//recover from inputlist + if(mode & RECOVER_LIST){ + recover_list(des_dir, input_filename,t_after,t_before,(recovermodus & RECOV_ALL )? 0 : 1); + mode = 0; + } + + +// search inodeNr from pathName use journal + if (mode & COMMAND_PATHNAME){ + if(! strcmp(pathname,"/") || ! strcmp(pathname,"")){ + inode_nr = EXT2_ROOT_INO ; + *pathname = 0; + } + else{ + int l=strlen(pathname) -1 ; + if (*(pathname + l) == '/') + *(pathname + l) = 0 ; + inode_nr = local_namei(NULL,pathname,t_after,t_before,DELETED_OPT); + } + if (inode_nr) { + printf("Inode found \"%s\" %lu \n", pathname, inode_nr); + } + else{ + fprintf(stderr,"Error: Inode not found for \"%s\"\n",pathname); + fprintf(stderr,"Check the valid PATHNAME \"%s\" and the BEFORE option \"%s\"\n", pathname,time_to_string(t_before)); + exitval = EXIT_FAILURE ; + goto errout; + } + } + else{ + if (mode & COMMAND_INODE){ + pathname = malloc(20); + if (inode_nr == EXT2_ROOT_INO) + *pathname = 0; + else + sprintf(pathname,"<%lu>",inode_nr); + } + } + + +// print journal transaction bocklist + if (mode & PRINT_BLOCKLIST){ + if (mode & COMMAND_BLOCK) + print_block_transaction((blk64_t)block_nr,format); + if (mode & COMMAND_INODE){ + struct inode_pos_struct pos; + block_nr = get_inode_pos(current_fs->super, &pos , inode_nr, 1); + print_block_transaction((blk64_t)block_nr,format); + } + if (!(mode & (COMMAND_INODE + COMMAND_BLOCK))) print_block_list( format ); + } + + +// print journal superblock + if (mode & PRINT_J_SUPERBLOCK) dump_journal_superblock(); + + +//print journal block + if ((mode & COMMAND_BLOCK) && (mode & PRINT_TRANSACTION )){ + __u32 journal_block; + journal_block = get_journal_blocknr(block_nr, transaction_nr); + if ( journal_block ){ + printf("dump Journalblock %lu : a copy of Filesystemblock %lu : Transaction %lu\n", + journal_block, block_nr, transaction_nr); + dump_journal_block( journal_block, format ); + } + else + fprintf(stderr,"Error: Filesystemblock %lu not found in Journaltransaction %lu\n",block_nr, transaction_nr); + } + + +//print or recover a single inode from transactionnumber +if ((mode & COMMAND_INODE) && (mode & PRINT_TRANSACTION )){ + struct ext2_inode_large *inode; + inode = malloc( current_fs->blocksize ); + if (inode){ + if (! get_transaction_inode(inode_nr, transaction_nr, inode)) + print_j_inode(inode , inode_nr , transaction_nr , format); + if ((mode & RECOVER_INODE) && (recovermodus & (RECOV_ALL | RECOV_DEL)) && (!(LINUX_S_ISDIR(inode->i_mode)))){ + recover_file(des_dir,"", pathname,(struct ext2_inode*)inode, inode_nr ,(recovermodus & RECOV_ALL )? 0 : 1 ); + mode = 0; + } + free(inode); + } +} + + +// start recursiv over the filesystem, used for recover, list and tree-history +if ((mode & COMMAND_INODE) && (mode & RECOVER_INODE)) + { + struct ring_buf *i_list; + struct ext2_inode* r_inode; + r_item *item = NULL; + + if (ext2fs_test_inode_bitmap ( current_fs->inode_map, inode_nr )) { + fprintf(stdout,"Inode %lu is allocated\n",inode_nr); + } + + i_list = get_j_inode_list(current_fs->super, inode_nr); + + if (mode & INPUT_TIME) + item = get_undel_inode(i_list,t_after,t_before); + else + item = get_last_undel_inode(i_list); + + + if (item) { + r_inode = (struct ext2_inode*)item->inode; + if (LINUX_S_ISDIR(r_inode->i_mode) ) + { + struct dir_list_head_t * dir = NULL; + + dir = get_dir3(NULL,0, inode_nr , "",pathname, t_after,t_before, recoverquality ); + if (dir) { +//FIXME: recovermodus + lookup_local(des_dir, dir,t_after,t_before, recoverquality | recovermodus ); + if (recovermodus & HIST_DIR ) + print_coll_list(t_after, t_before, format); + } + else + printf("Inode %lu is a directory but not found after %lu and before %lu\n",inode_nr,t_after,t_before); + + if (dir) clear_dir_list(dir); + } + else { + if (recovermodus & (RECOV_ALL | RECOV_DEL)) + recover_file(des_dir,"", pathname, r_inode, inode_nr ,(recovermodus & RECOV_ALL )? 0 : 1 ); + else + printf("Single file can only recovered with option -R or -r\n"); + } + } + else + fprintf(stdout,"No undeled inode %u in journal found\n",inode_nr); + + if (i_list) ring_del(i_list); + } + + + +//print all journal inode + if ((mode & COMMAND_INODE) && !(mode & RECOVER_INODE) && !(mode & PRINT_TRANSACTION)) + { + struct ring_buf* i_list; + r_item *item = NULL; + + i_list = get_j_inode_list(current_fs->super, inode_nr); + if (i_list){ + if (mode & INPUT_TIME){ + item = get_undel_inode(i_list,t_after,t_before); + if(item){ + dump_inode(stdout, "",inode_nr, (struct ext2_inode *)item->inode, format); + if ( LINUX_S_ISDIR(item->inode->i_mode)) + list_dir3(inode_nr, (struct ext2_inode*) item->inode, &(item->transaction)); + } + else + printf("No entry with this time found\n"); + } + else{ + dump_inode_list(i_list, format); + } + if (i_list) ring_del(i_list); + } + } + + if(!journal_flag) journal_flag = journal_close(); + }// end open Journal +} // end Operation + +exitval = EXIT_SUCCESS; + +errout: + if (current_fs) { + retval = ext2fs_close(current_fs); + if (retval) { + fprintf(stderr, "ext2fs_close\n"); + current_fs = NULL; + } + } + if (pathname) free(pathname); + clear_link_stack(); + return exitval; +} +//------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/ext4magic.h b/src/ext4magic.h new file mode 100644 index 0000000..31442f4 --- /dev/null +++ b/src/ext4magic.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef EXT4MAGIC_H +#define EXT4MAGIC_H + +// ext4magic mode for navigation main() +#define PRINT_SUPERBLOCK 0x0001 +#define PRINT_J_SUPERBLOCK 0x0002 +#define COMMAND_INODE 0x0004 +#define COMMAND_BLOCK 0x0008 +#define COMMAND_PATHNAME 0x0010 +#define PRINT_TRANSACTION 0x0020 +#define PRINT_BLOCKLIST 0x0040 +#define PRINT_HISTOGRAM 0x0080 +#define INPUT_TIME 0x0100 +#define HIGH_QUALITY 0x0200 +#define READ_JOURNAL 0x1000 +#define RECOVER_INODE 0x2000 +#define RECOVER_LIST 0x4000 + +// journal status flags +#define JOURNAL_OPEN 0 +#define JOURNAL_CLOSE 1 +#define JOURNAL_ERROR 2 + +#endif + + diff --git a/src/hard_link_stack.c b/src/hard_link_stack.c new file mode 100644 index 0000000..85af300 --- /dev/null +++ b/src/hard_link_stack.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//construct for global collect of hardlinks + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include "hard_link_stack.h" + +static struct link_stack_head head; + +void init_link_stack(){ + head.count = 0; + head.begin = NULL; + head.pointer = NULL; +} + + +void add_link_stack(ext2_ino_t inode_nr, __u32 link_count, char* name, __u32 generation){ + struct link_entry* this; + + this = malloc (sizeof(struct link_entry)); + if (!this) + goto errout; + this->inode_nr = inode_nr; + this->link_count = link_count -1 ; + this->name = malloc(strlen(name) +1); + this->generation = generation; + if (!this->name) + goto errout; + strcpy(this->name,name); + this->next = head.begin; + head.begin = this; + head.count++; +return; + +errout: + if(this->name) + free(this->name); + if(this) + free(this); +} + + +char* check_link_stack(ext2_ino_t inode_nr, __u32 generation){ + + head.pointer = head.begin; + while (head.pointer) { + if((head.pointer->inode_nr == inode_nr) && (head.pointer->generation == generation)) + break; + head.pointer = head.pointer->next; + } + return (head.pointer) ? head.pointer->name : NULL ; +} + + +static void del_link_stack(struct link_entry* entry){ + if(entry->name) + free(entry->name); + head.pointer = head.begin; + if (head.begin->next){ + while ((head.pointer->next) && (head.pointer != entry) && (head.pointer->next != entry)) + head.pointer = head.pointer->next; + if(head.begin == entry) + head.begin = entry->next; + else + head.pointer->next = entry->next; + } + else + head.begin = NULL; + free(entry); + head.count--; +} + + +int match_link_stack(ext2_ino_t inode_nr, __u32 generation){ + int retval = 1; + if ((head.pointer->inode_nr == inode_nr) && (head.pointer->generation == generation)){ +// if (! --(head.pointer->link_count)) +// del_link_stack(head.pointer); + (head.pointer->link_count)-- ; + retval = 0; + } +return retval; +} + + +void clear_link_stack(){ + int d_count = 0 ; + + fflush(stdout); + if (head.count){ + fprintf(stderr,"Hardlink Database\n", head.count); + + head.pointer = head.begin; + while (head.pointer){ + if(head.pointer->link_count){ + fprintf(stderr,"%10d\t%s\n",head.pointer->link_count,head.pointer->name); + d_count++ ; + } + if(head.pointer->name) + free(head.pointer->name); + head.begin = head.pointer->next; + free(head.pointer); + head.pointer = head.begin; + } + if (! d_count) + fprintf(stderr,"all Hardlinks be resolved\n"); + } +} + + +
\ No newline at end of file diff --git a/src/hard_link_stack.h b/src/hard_link_stack.h new file mode 100644 index 0000000..eea5ed7 --- /dev/null +++ b/src/hard_link_stack.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +//construct for global collect of hardlinks + +#ifndef HARD_LINKSTACK_H +#define HARD_LINKSTACK_H +#include <ext2fs/ext2fs.h> + +struct link_entry{ + ext2_ino_t inode_nr; + __u32 generation; + int link_count; + char* name; + struct link_entry* next; +}; + +struct link_stack_head{ + __u32 count; + struct link_entry* begin; + struct link_entry* pointer; +}; + + +void init_link_stack(); +void add_link_stack(ext2_ino_t , __u32, char*, __u32 ); +char* check_link_stack(ext2_ino_t, __u32); +int match_link_stack(ext2_ino_t, __u32 ); +void clear_link_stack(); + + +#endif
\ No newline at end of file diff --git a/src/inode.c b/src/inode.c new file mode 100644 index 0000000..d617775 --- /dev/null +++ b/src/inode.c @@ -0,0 +1,805 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "inode.h" +#include "ring_buf.h" + +extern ext2_filsys current_fs; + + + +// read real fs inode 128++ +int intern_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode, int bufsize) +{ + int retval; + + retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize); + if (retval) { + fprintf(stderr,"Error %d while reading inode %u\n",retval, ino); + return 1; + } + return 0; +} + + +// read real fs inode 128 +int intern_read_inode(ext2_ino_t ino, struct ext2_inode * inode) +{ + int retval; + + retval = ext2fs_read_inode(current_fs, ino, inode); + if (retval) { + fprintf(stderr,"Error %d while reading inode %u\n",retval, ino); + return 1; + } + return 0; +} + + +//#ifdef WORDS_BIGENDIAN +// On my current version of libext2 the extra time fields ar not bigendian corrected +// We want this solved temporarily here with this function + static void le_to_cpu_swap_extra_time(struct ext2_inode_large *inode, char *inode_buf){ + //inode->i_pad1 = ext2fs_le16_to_cpu(((struct ext2_inode_large *))inode_buf->i_pad1); + inode->i_ctime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_ctime_extra); + inode->i_mtime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_mtime_extra ); + inode->i_atime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_atime_extra ); + inode->i_crtime = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_crtime ); + inode->i_crtime_extra = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_crtime_extra ); + //inode->i_version_hi = ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_version_hi ); +} +//#endif + +//subfunction for dump_inode_extra +static void dump_xattr_string(FILE *out, const char *str, int len) +{ + int printable = 0; + int i; + + // check: is string "printable enough?" + for (i = 0; i < len; i++) + if (isprint(str[i])) + printable++; + + if (printable <= len*7/8) + printable = 0; + + for (i = 0; i < len; i++) + if (printable) + fprintf(out, isprint(str[i]) ? "%c" : "\\%03o", + (unsigned char)str[i]); + else + fprintf(out, "%02x ", (unsigned char)str[i]); +} + + +//print Blocks of inode (ext4) +static void local_dump_extents(FILE *f, const char *prefix, struct ext2_inode * inode, + int flags, int logical_width, int physical_width) +{ + ext2_extent_handle_t handle; + struct ext2fs_extent extent; + struct ext2_extent_info info; + int op = EXT2_EXTENT_ROOT; + unsigned int printed = 0; + errcode_t errcode; + + errcode = local_ext2fs_extent_open(current_fs, *inode, &handle); + if (errcode) + return; + + if (flags & DUMP_EXTENT_TABLE) + fprintf(f, "Level Entries %*s %*s Length Flags\n", + (logical_width*2)+3, "Logical", + (physical_width*2)+3, "Physical"); + else + fprintf(f, "%sEXTENTS:\n%s", prefix, prefix); + + while (1) { + errcode = ext2fs_extent_get(handle, op, &extent); + + if (errcode) + break; + + op = EXT2_EXTENT_NEXT; + + if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + continue; + + if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) { + if ((flags & DUMP_LEAF_EXTENTS) == 0) + continue; + } else { + if ((flags & DUMP_NODE_EXTENTS) == 0) + continue; + } + + + errcode = ext2fs_extent_get_info(handle, &info); + if (errcode) + continue; + + if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) { + if (extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) + continue; + + if (flags & DUMP_EXTENT_TABLE) { + fprintf(f, "%2d/%2d %3d/%3d %*llu - %*llu " + "%*llu%*s %6u\n", + info.curr_level, info.max_depth, + info.curr_entry, info.num_entries, + logical_width, + extent.e_lblk, + logical_width, + extent.e_lblk + (extent.e_len - 1), + physical_width, + extent.e_pblk, + physical_width+3, "", extent.e_len); + continue; + } + + fprintf(f, "%s(NODE #%d, %lld-%lld, blk %lld)", + printed ? ", " : "", + info.curr_entry, + extent.e_lblk, + extent.e_lblk + (extent.e_len - 1), + extent.e_pblk); + printed = 1; + continue; + } + + if (flags & DUMP_EXTENT_TABLE) { + fprintf(f, "%2d/%2d %3d/%3d %*llu - %*llu " + "%*llu - %*llu %6u %s\n", + info.curr_level, info.max_depth, + info.curr_entry, info.num_entries, + logical_width, + extent.e_lblk, + logical_width, + extent.e_lblk + (extent.e_len - 1), + physical_width, + extent.e_pblk, + physical_width, + extent.e_pblk + (extent.e_len - 1), + extent.e_len, + extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ? + "Uninit" : ""); + continue; + } + + if (extent.e_len == 0) + continue; + else if (extent.e_len == 1) + fprintf(f, + "%s(%lld%s): %lld", + printed ? ", " : "", + extent.e_lblk, + extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ? + " [uninit]" : "", + extent.e_pblk); + else + fprintf(f, + "%s(%lld-%lld%s): %lld-%lld", + printed ? ", " : "", + extent.e_lblk, + extent.e_lblk + (extent.e_len - 1), + extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT ? + " [uninit]" : "", + extent.e_pblk, + extent.e_pblk + (extent.e_len - 1)); + printed = 1; + } + if (printed) + fprintf(f, "\n\n"); + local_ext2fs_extent_free(handle); + + +} + + +//print extended attribute of Inode +static void dump_inode_extra(FILE *out, + const char *prefix EXT2FS_ATTR((unused)), + ext2_ino_t inode_num EXT2FS_ATTR((unused)), + struct ext2_inode_large *inode) +{ + struct ext2_ext_attr_entry *entry; + __u32 *magic; + char *start, *end; + unsigned int storage_size; + + fprintf(out, "Size of extra inode fields: %u\n", inode->i_extra_isize); + if (inode->i_extra_isize > EXT2_INODE_SIZE(current_fs->super) - + EXT2_GOOD_OLD_INODE_SIZE) { + fprintf(stderr, "invalid inode->i_extra_isize (%u)\n", + inode->i_extra_isize); + return; + } + storage_size = EXT2_INODE_SIZE(current_fs->super) - + EXT2_GOOD_OLD_INODE_SIZE - + inode->i_extra_isize; + magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE + + inode->i_extra_isize); + if (*magic == EXT2_EXT_ATTR_MAGIC) { + fprintf(out, "Extended attributes stored in inode body: \n"); + end = (char *) inode + EXT2_INODE_SIZE(current_fs->super); + start = (char *) magic + sizeof(__u32); + entry = (struct ext2_ext_attr_entry *) start; + while (!EXT2_EXT_IS_LAST_ENTRY(entry)) { + struct ext2_ext_attr_entry *next = + EXT2_EXT_ATTR_NEXT(entry); + if (entry->e_value_size > storage_size || + (char *) next >= end) { + fprintf(out, "invalid EA entry in inode\n"); + return; + } + fprintf(out, " "); + dump_xattr_string(out, EXT2_EXT_ATTR_NAME(entry), + entry->e_name_len); + fprintf(out, " = \""); + dump_xattr_string(out, start + entry->e_value_offs, + entry->e_value_size); + fprintf(out, "\" (%u)\n", entry->e_value_size); + entry = next; + } + } +} + + +//subfunction for dump_blocks +static void finish_range(struct list_blocks_struct *lb) +{ + if (lb->first_block == 0) + return; + if (lb->first) + lb->first = 0; + else + fprintf(lb->f, ", "); + if (lb->first_block == lb->last_block) + fprintf(lb->f, "(%lld):%u", + (long long)lb->first_bcnt, lb->first_block); + else + fprintf(lb->f, "(%lld-%lld):%u-%u", + (long long)lb->first_bcnt, (long long)lb->last_bcnt, + lb->first_block, lb->last_block); + lb->first_block = 0; +} + + +//subfunction for dump_blocks +static int list_blocks_proc(ext2_filsys fs EXT2FS_ATTR((unused)), + blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *private) +{ + struct list_blocks_struct *lb = (struct list_blocks_struct *) private; + + lb->total++; + if (blockcnt >= 0) { +//* See if we can add on to the existing range (if it exists) + if (lb->first_block && + (lb->last_block+1 == *blocknr) && + (lb->last_bcnt+1 == blockcnt)) { + lb->last_block = *blocknr; + lb->last_bcnt = blockcnt; + return 0; + } +//* Start a new range. + finish_range(lb); + lb->first_block = lb->last_block = *blocknr; + lb->first_bcnt = lb->last_bcnt = blockcnt; + return 0; + } +//* Not a normal block. Always force a new range. + finish_range(lb); + if (lb->first) + lb->first = 0; + else + fprintf(lb->f, ", "); + if (blockcnt == -1) + fprintf(lb->f, "(IND):%u", *blocknr); + else if (blockcnt == -2) + fprintf(lb->f, "(DIND):%u", *blocknr); + else if (blockcnt == -3) + fprintf(lb->f, "(TIND):%u", *blocknr); + return 0; +} + + +// print the Datablocks from Inode (ext3) +static void dump_blocks(FILE *f, const char *prefix, struct ext2_inode *inode) +{ + struct list_blocks_struct lb; + + fprintf(f, "%sBLOCKS:\n%s", prefix, prefix); + lb.total = 0; + lb.first_block = 0; + lb.f = f; + lb.first = 1; + // ext2fs_block_iterate2(current_fs, inode, BLOCK_FLAG_READ_ONLY, NULL, + local_block_iterate3(current_fs, *inode, BLOCK_FLAG_READ_ONLY, NULL, + list_blocks_proc, (void *)&lb); + finish_range(&lb); + if (lb.total) + fprintf(f, "\n%sTOTAL: %lld\n", prefix, (long long)lb.total); + fprintf(f,"\n"); +} + + +//print the contents of inode +void dump_inode(FILE *out, const char *prefix, + ext2_ino_t inode_num, struct ext2_inode *inode, + int do_dump_blocks) +{ + const char *i_type; + char frag, fsize; + int os = current_fs->super->s_creator_os; + struct ext2_inode_large *large_inode; + int is_large_inode = 0; + + if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE) + is_large_inode = 1; + large_inode = (struct ext2_inode_large *) inode; +// blockhex(stdout,large_inode,0,256); + + if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory"; + else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular"; + else if (LINUX_S_ISLNK(inode->i_mode)) i_type = "symlink"; + else if (LINUX_S_ISBLK(inode->i_mode)) i_type = "block special"; + else if (LINUX_S_ISCHR(inode->i_mode)) i_type = "character special"; + else if (LINUX_S_ISFIFO(inode->i_mode)) i_type = "FIFO"; + else if (LINUX_S_ISSOCK(inode->i_mode)) i_type = "socket"; + else i_type = "bad type"; + fprintf(out, "%sInode: %u Type: %s ", prefix, inode_num, i_type); + fprintf(out, "%sMode: %04o Flags: 0x%x\n", + prefix, inode->i_mode & 0777, inode->i_flags); + if (is_large_inode && large_inode->i_extra_isize >= 24) { + fprintf(out, "%sGeneration: %u Version: 0x%08x:%08x\n", + prefix, inode->i_generation, large_inode->i_version_hi, + inode->osd1.linux1.l_i_version); + } else { + fprintf(out, "%sGeneration: %u Version: 0x%08x\n", prefix, + inode->i_generation, inode->osd1.linux1.l_i_version); + } + fprintf(out, "%sUser: %5d Group: %5d Size: ", + prefix, inode_uid(*inode), inode_gid(*inode)); + if (LINUX_S_ISREG(inode->i_mode)) { + unsigned long long i_size = (inode->i_size | + ((unsigned long long)inode->i_size_high << 32)); + + fprintf(out, "%llu\n", i_size); + } else + fprintf(out, "%d\n", inode->i_size); + if (os == EXT2_OS_HURD) + fprintf(out, + "%sFile ACL: %d Directory ACL: %d Translator: %d\n", + prefix, + inode->i_file_acl, LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0, + inode->osd1.hurd1.h_i_translator); + else + fprintf(out, "%sFile ACL: %llu Directory ACL: %d\n", + prefix, + inode->i_file_acl | ((long long) + (inode->osd2.linux2.l_i_file_acl_high) << 32), + LINUX_S_ISDIR(inode->i_mode) ? inode->i_dir_acl : 0); + if (os == EXT2_OS_LINUX) + fprintf(out, "%sLinks: %d Blockcount: %llu\n", + prefix, inode->i_links_count, + (((unsigned long long) + inode->osd2.linux2.l_i_blocks_hi << 32)) + + inode->i_blocks); + else + fprintf(out, "%sLinks: %d Blockcount: %u\n", + prefix, inode->i_links_count, inode->i_blocks); + switch (os) { + case EXT2_OS_HURD: + frag = inode->osd2.hurd2.h_i_frag; + fsize = inode->osd2.hurd2.h_i_fsize; + break; + default: + frag = fsize = 0; + } + fprintf(out, "%sFragment: Address: %d Number: %d Size: %d\n", + prefix, inode->i_faddr, frag, fsize); + if (is_large_inode && large_inode->i_extra_isize >= 24) { + fprintf(out, "%s ctime: %10lu:%010lu -- %s", prefix, + inode->i_ctime, large_inode->i_ctime_extra, + time_to_string(inode->i_ctime)); + fprintf(out, "%s atime: %10lu:%010lu -- %s", prefix, + inode->i_atime, large_inode->i_atime_extra, + time_to_string(inode->i_atime)); + fprintf(out, "%s mtime: %10lu:%010lu -- %s", prefix, + inode->i_mtime, large_inode->i_mtime_extra, + time_to_string(inode->i_mtime)); + fprintf(out, "%scrtime: %10lu:%010lu -- %s", prefix, + large_inode->i_crtime, large_inode->i_crtime_extra, + time_to_string(large_inode->i_crtime)); + } else { + fprintf(out, "%sctime: %10lu -- %s", prefix, inode->i_ctime, + time_to_string(inode->i_ctime)); + fprintf(out, "%satime: %10lu -- %s", prefix, inode->i_atime, + time_to_string(inode->i_atime)); + fprintf(out, "%smtime: %10lu -- %s", prefix, inode->i_mtime, + time_to_string(inode->i_mtime)); + } + if (inode->i_dtime) + fprintf(out, "%sdtime: %10lu -- %s", prefix, inode->i_dtime, + time_to_string(inode->i_dtime)); + if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE) + dump_inode_extra(out, prefix, inode_num, + (struct ext2_inode_large *) inode); + if (LINUX_S_ISLNK(inode->i_mode) && ext2fs_inode_data_blocks(current_fs,inode) == 0) + fprintf(out, "%sFast_link_dest: %.*s\n\n", prefix, + (int) inode->i_size, (char *)inode->i_block); + else if (LINUX_S_ISBLK(inode->i_mode) || LINUX_S_ISCHR(inode->i_mode)) { + int major, minor; + const char *devnote; + + if (inode->i_block[0]) { + major = (inode->i_block[0] >> 8) & 255; + minor = inode->i_block[0] & 255; + devnote = ""; + } else { + major = (inode->i_block[1] & 0xfff00) >> 8; + minor = ((inode->i_block[1] & 0xff) | + ((inode->i_block[1] >> 12) & 0xfff00)); + devnote = "(New-style) "; + } + fprintf(out, "%sDevice major/minor number: %02d:%02d (hex %02x:%02x)\n\n", + devnote, major, minor, major, minor); + } else if (do_dump_blocks) { + if (inode->i_flags & EXT4_EXTENTS_FL) + local_dump_extents(out, prefix, inode, + DUMP_LEAF_EXTENTS, 0, 0); + else + dump_blocks(out, prefix, inode); + } +} + + +//calculate the position of inode in FS +blk_t get_inode_pos(struct ext2_super_block *es ,struct inode_pos_struct *pos, ext2_ino_t inode_nr,int flag){ + + __u32 inode_group, group_offset, inodes_per_block, inode_offset; + blk_t inode_block; + + pos->size = EXT2_INODE_SIZE(current_fs->super); + inode_group = ((inode_nr - 1) / es->s_inodes_per_group); + group_offset = ((inode_nr - 1) % es->s_inodes_per_group); + inodes_per_block = (current_fs->blocksize / pos->size ); + + inode_block = current_fs->group_desc[inode_group].bg_inode_table + (group_offset / inodes_per_block); + inode_offset = ((group_offset % inodes_per_block) * pos->size ); + if (flag) + printf("Inode %u is at group %u, block %u, offset %u\n", inode_nr, inode_group, inode_block, inode_offset); + + pos->block = inode_block; + pos->offset = inode_offset; + + return inode_block; +}; + + +// get journalinode from transactionnumber +int get_transaction_inode(ext2_ino_t inode_nr, int transaction_nr, struct ext2_inode_large *inode){ + struct inode_pos_struct pos; + __u32 journal_block; + blk_t block_nr; + struct ext2_inode_large *inode_buf; + char *buf = NULL; + int got,retval = 0; + int blocksize = current_fs->blocksize; + + block_nr = get_inode_pos(current_fs->super, &pos , inode_nr, 0); + journal_block = get_journal_blocknr(block_nr, transaction_nr); + if (! journal_block){ + fprintf(stdout,"No journalblock found for inode %u by transaction %u\n",inode_nr,transaction_nr); + retval = -1; + } + else { + buf =(char*) malloc(blocksize); + if(buf){ + //inode = (struct ext2_inode_large *)(buf + blocksize); + retval = read_journal_block(journal_block * blocksize ,buf,blocksize,&got); + if ((! retval) && (got == blocksize)){ + inode_buf = (struct ext2_inode_large *)(buf + pos.offset); +#ifdef WORDS_BIGENDIAN + memset(inode, 0, pos.size); + ext2fs_swap_inode_full(current_fs, inode, inode_buf, 0, pos.size); + + if ((pos.size > EXT2_GOOD_OLD_INODE_SIZE) && (inode->i_extra_isize >= 24) + && (ext2fs_le32_to_cpu(inode_buf->i_crtime ) != inode->i_crtime)){ +//FIXME: On my current version of libext2 the extra time fields ar not bigendian corrected + // We solved this temporarily here with this function + le_to_cpu_swap_extra_time(inode,(char*)inode_buf); + } +#else + memcpy(inode, inode_buf, pos.size); +#endif + } + free(buf); + } + } +return retval; +} + + +//function for dump_inode_list +void print_j_inode(struct ext2_inode_large *inode , ext2_ino_t inode_nr , __u32 transaction , int flag){ + fprintf(stdout,"\nDump Inode %d from journal transaction %d\n",inode_nr,transaction); + dump_inode(stdout, "",inode_nr, (struct ext2_inode *)inode, flag); +return ; +} + + + +//print the contents of all copy of inode in the journal +void dump_inode_list(struct ring_buf* buf, int flag){ + r_item* item; + int i, count; + + if (!buf ) return; + item = r_first(buf); + + count = r_get_count(buf); + for (i = 0; i < count ; i++){ + print_j_inode(item->inode , buf->nr , item->transaction.start, flag); + if ( LINUX_S_ISDIR(item->inode->i_mode)) + list_dir3(buf->nr, (struct ext2_inode*)item->inode, &(item->transaction)); + item = r_next(item); + } +return; +} + + +// return the last undeleted inode in journal +r_item* get_last_undel_inode(struct ring_buf* buf){ + r_item* item; + int i, count; +// __u32 generation; + + if (!buf ) return NULL; + item = r_last(buf); + + count = r_get_count(buf); + for (i = 0; i< count; i++){ +// if ( !i ) generation = item->inode->i_generation; + if (item->inode->i_dtime) { +// buf->del_flag = 1; + item = r_prev(item); + } + else { +// if (item->inode->i_generation != generation) +// buf->reuse_flag = 1; + return item; + } + } + return NULL; +} + + +// return the last undelete inode in journal after -> <-before + r_item* get_undel_inode(struct ring_buf* buf, __u32 after, __u32 before){ + r_item* item; + int i, count; + __u32 generation; + + if (!buf) return NULL; + item = r_last(buf); + + count = r_get_count(buf); + for (i = 0; i< count; i++){ +// if ( !i ) generation = item->inode->i_generation; + if ((item->inode->i_dtime) && (item->inode->i_dtime < after) ) + return NULL; + if ((item->inode->i_ctime >= before ) || (item->inode->i_dtime)) { +// if (item->inode->i_dtime) +// buf->del_flag = 1 ; + item = r_prev(item); + continue; + } +// if (item->inode->i_generation != generation) +// buf->reuse_flag = 1; + return item; + } + return NULL; +} + + +//fill all inode found in the Journal in the inode-ringbuffer +struct ring_buf* get_j_inode_list(struct ext2_super_block *es, ext2_ino_t inode_nr){ + struct inode_pos_struct pos; + blk_t block; + char * inode_buf = NULL ; + struct ext2_inode *inode_pointer; + struct ring_buf* buf = NULL; + r_item *item = NULL; +// struct ext2_inode_large *inode = NULL; + int count, got, retval = 0; + off_t offset; + char *journal_tag_buf = NULL; + journal_descriptor_tag_t *block_list; + __u32 ctime = 1; + __u32 same_size = 1; + __u16 same_link_count = 0; + + if ((inode_nr > es->s_inodes_count) || (inode_nr == 0)) + { + printf(" unknown ERROR: bad inode number found %d \n", inode_nr ); + return NULL; + } + + block = get_inode_pos(es , &pos , inode_nr, 0); + inode_buf = malloc(pos.size); + if (!inode_buf) { + fprintf(stderr,"Error: can not allocate memory for buffer\n"); + goto errout; + } + count = get_block_list_count(block) ; + + if(! count) { +// no inode block found +// then we will load the oginal inode from the filesystem +// we will hope, the file has not change for a long time + buf = ring_new(pos.size,inode_nr); + if (buf) + item = r_item_add(buf); + if ( ! item) + { + fprintf(stderr,"Error: can not allocate memory for inode\n"); + goto errout; + } + if ( ext2fs_read_inode_full(current_fs, inode_nr, (struct ext2_inode*) item->inode, pos.size)) + goto errout; + item->transaction.start = item->transaction.end = 0; + } + else { + +// read and fill the journal inode + journal_tag_buf = (void *)malloc((sizeof(journal_descriptor_tag_t) * count)); + if (!journal_tag_buf) { + fprintf(stderr,"Error: while allocate Memory for blocklist\n"); + goto errout; + } + block_list = (journal_descriptor_tag_t *) journal_tag_buf; + + count = get_block_list(block_list, block, count); + buf = ring_new(pos.size,inode_nr); + if ((! buf) || (! count)) goto errout; + + for (;count > 0;count-- , block_list++ ){ + offset = (block_list->j_blocknr * current_fs->blocksize) + pos.offset ; + retval = read_journal_block( offset , inode_buf , pos.size , &got); + if (retval) { + fprintf(stderr,"Error: while read Inode %d from journal transaction %d\n", inode_nr, block_list->transaction); + goto errout; + } + + inode_pointer = (struct ext2_inode*) inode_buf ; +//FIXME: check more bad Inode + if (get_inode_mode_type(ext2fs_le16_to_cpu(inode_pointer->i_mode)) == ' '){ +#ifdef DEBUG + fprintf(stdout,"Transaction %d has a bad Inode, skip\n",block_list->transaction); +#endif + continue; + } +//inode with the same ctime and the same size an links, skipped + if (ext2fs_le32_to_cpu(inode_pointer->i_ctime) == ctime){ + if (item) { + if ((ext2fs_le32_to_cpu(inode_pointer->i_size) == same_size) && + (ext2fs_le16_to_cpu(inode_pointer->i_links_count == same_link_count))){ + item->transaction.end = block_list->transaction; + continue; + } + } + } + + item = r_item_add(buf); + if ( ! item) + { + fprintf(stderr,"Error: can not allocate memory for inode\n"); + goto errout; + } + +#ifdef WORDS_BIGENDIAN + memset(item->inode, 0, pos.size); + + ext2fs_swap_inode_full(current_fs, + (struct ext2_inode_large *) item->inode, + (struct ext2_inode_large *) inode_buf, + 0, pos.size); + + if ((pos.size > EXT2_GOOD_OLD_INODE_SIZE) && (item->inode->i_extra_isize >= 24) + && (ext2fs_le32_to_cpu(((struct ext2_inode_large *)inode_buf)->i_crtime) != item->inode->i_crtime)){ +//FIXME: On my current version of libext2 the extra time fields ar not bigendian corrected + // We solved this temporarily here with this function + le_to_cpu_swap_extra_time(item->inode,(char*)inode_buf); + } +#else + memcpy(item->inode, inode_buf, pos.size); +#endif + item->transaction.start = block_list->transaction; + item->transaction.end = block_list->transaction; + ctime = item->inode->i_ctime; + same_size = item->inode->i_size; + same_link_count = item->inode->i_links_count; + } + } // else-tree ? count==0 + + if (buf->count == 0) + goto errout; + + if ( inode_buf ) { + free(inode_buf); + inode_buf = NULL; + } + if ( journal_tag_buf ) { + free(journal_tag_buf); + journal_tag_buf = NULL; + } + r_begin(buf); + return buf; + +errout: + if ( inode_buf ) { + free(inode_buf); + inode_buf = NULL; + } + if ( journal_tag_buf ) { + free(journal_tag_buf); + journal_tag_buf = NULL; + } + + if ( buf ) { + ring_del(buf); + buf = NULL; + } + return NULL; +} + + + +// get the first Journal Inode by transaction +int read_journal_inode( ext2_ino_t inode_nr, struct ext2_inode* inode_buf, __u32 transaction){ + struct ring_buf* i_ring; + r_item* item; + int retval = 1; + + i_ring = get_j_inode_list(current_fs->super, inode_nr); + if (i_ring) { + item = r_first(i_ring); + while ((item->transaction.start < transaction) && ( item != r_last(i_ring))) + item = r_next(item); + memcpy(inode_buf,item->inode,EXT2_GOOD_OLD_INODE_SIZE); + ring_del(i_ring); + retval= 0; + } +return retval; +} diff --git a/src/inode.h b/src/inode.h new file mode 100644 index 0000000..3fe22e6 --- /dev/null +++ b/src/inode.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef INODE_H +#define INODE_H + + +#include <ext2fs/ext2fs.h> +#include "util.h" +#include "ext4magic.h" +#include "journal.h" +#include "ring_buf.h" + +#define DUMP_LEAF_EXTENTS 0x01 +#define DUMP_NODE_EXTENTS 0x02 +#define DUMP_EXTENT_TABLE 0x04 + + +//help struct for list inode data blocks +struct list_blocks_struct { + FILE *f; + e2_blkcnt_t total; + blk_t first_block, last_block; + e2_blkcnt_t first_bcnt, last_bcnt; + e2_blkcnt_t first; +}; + + + +//private an helper functions +static void dump_xattr_string(FILE*, const char*, int);//subfunction for dump_inode_extra +static void local_dump_extents(FILE*, const char*, struct ext2_inode *,int , int, int );//print Blocks of inode (ext4) +static void dump_inode_extra(FILE*, const char* , ext2_ino_t, struct ext2_inode_large*);//print extended attribute of Inode +static void finish_range(struct list_blocks_struct*);//subfunction for dump_blocks +static int list_blocks_proc(ext2_filsys, blk_t* , e2_blkcnt_t,blk_t, int, void*);//subfunction for dump_blocks +static void dump_blocks(FILE*, const char*, struct ext2_inode *);// print the Datablocks from Inode (ext3) + + +//functions for external use +int intern_read_inode_full(ext2_ino_t, struct ext2_inode*, int);// read real fs inode 128++ +int intern_read_inode(ext2_ino_t, struct ext2_inode*);// read real fs inode 128 +r_item* get_undel_inode(struct ring_buf*, __u32, __u32);// return the last undelete inode in journal after -> <-before +r_item* get_last_undel_inode(struct ring_buf* );// return the last undeleted inode in journal +blk_t get_inode_pos(struct ext2_super_block* , struct inode_pos_struct*, ext2_ino_t, int);//calculate the position of inode in FS +void print_j_inode(struct ext2_inode_large* , ext2_ino_t , __u32, int );//function for dump_inode_list +int get_transaction_inode(ext2_ino_t, int, struct ext2_inode_large*);// get journalinode from transactionnumber +void dump_inode_list(struct ring_buf* , int);//print the contents of all copy of inode in the journal +void dump_inode(FILE*, const char*, ext2_ino_t, struct ext2_inode*,int);//print the contents of inode +int read_journal_inode( ext2_ino_t, struct ext2_inode*, __u32);// get the first Journal Inode by transaction +struct ring_buf* get_j_inode_list(struct ext2_super_block*, ext2_ino_t);//fill all inode found in the Journal in the inode-ringbuffer + + +#endif diff --git a/src/jfs_compat.h b/src/jfs_compat.h new file mode 100644 index 0000000..024333b --- /dev/null +++ b/src/jfs_compat.h @@ -0,0 +1,70 @@ + +#ifndef _JFS_COMPAT_H +#define _JFS_COMPAT_H + +/* ext3/4 libraries */ +#include <ext2fs/ext2fs.h> + +#include "kernel-list.h" +#include <errno.h> +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#define printk printf +#define KERN_ERR "" +#define KERN_DEBUG "" + +#define READ 0 +#define WRITE 1 + +#define cpu_to_be32(n) htonl(n) +#define be32_to_cpu(n) ntohl(n) + +typedef unsigned int tid_t; +typedef struct journal_s journal_t; + +struct buffer_head; +struct inode; + +struct journal_s +{ + unsigned long j_flags; + int j_errno; + struct buffer_head * j_sb_buffer; + struct journal_superblock_s *j_superblock; + int j_format_version; + unsigned long j_head; + unsigned long j_tail; + unsigned long j_free; + unsigned long j_first, j_last; + kdev_t j_dev; + kdev_t j_fs_dev; + int j_blocksize; + unsigned int j_blk_offset; + unsigned int j_maxlen; + struct inode * j_inode; + tid_t j_tail_sequence; + tid_t j_transaction_sequence; + __u8 j_uuid[16]; + struct jbd_revoke_table_s *j_revoke; + tid_t j_failed_commit; +}; + +#define J_ASSERT(assert) \ + do { if (!(assert)) { \ + printf ("Assertion failure in %s() at %s line %d: " \ + "\"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # assert); \ + fatal_error(e2fsck_global_ctx, 0); \ + } } while (0) + +#define is_journal_abort(x) 0 + +#define BUFFER_TRACE(bh, info) do {} while (0) + +/* Need this so we can compile with configure --enable-gcc-wall */ +#ifdef NO_INLINE_FUNCS +#define inline +#endif + +#endif /* _JFS_COMPAT_H */ diff --git a/src/jfs_user.h b/src/jfs_user.h new file mode 100644 index 0000000..3a52123 --- /dev/null +++ b/src/jfs_user.h @@ -0,0 +1,8 @@ +#ifndef _JFS_USER_H +#define _JFS_USER_H + +typedef unsigned short kdev_t; + +#include "kernel-jbd.h" + +#endif /* _JFS_USER_H */ diff --git a/src/journal.c b/src/journal.c new file mode 100644 index 0000000..ec75956 --- /dev/null +++ b/src/journal.c @@ -0,0 +1,759 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.h> + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <utime.h> + +#include <blkid/blkid.h> +#include <ext2fs/ext2fs.h> +#include "jfs_user.h" +#include <uuid/uuid.h> + +#include "ext4magic.h" +#include "util.h" +#include "journal.h" + +enum journal_location {JOURNAL_IS_INTERNAL, JOURNAL_IS_EXTERNAL}; + +//flags for journaldescriptor control +#define ANY_BLOCK ((blk_t) -1) +#define WRAP_UNDEF 0 +#define WRAP_ON 1 +#define WRAP_OFF 2 +#define WRAP_READY 3 + + +struct journal_source +{ + enum journal_location where; + int fd; + ext2_file_t file; +}; + + +//static variable for work with journal +struct journal_source journal_source; +extern ext2_filsys current_fs ; +struct ext2_super_block *jsb_pointer; //pointer for find & open journal +char jsb_buffer[1024]; //buffer for journal-superblock (be_to_CPU) +void* pt_buff; /*pointer of the privat descriptor Table */ +journal_descriptor_tag_t *pt; /*pointer for descriptor Table*/ +__u32 pt_count; /*counter for privat descriptor Table */ + + +//print journal superblock +void dump_journal_superblock( void) +{ + int i; + __u32 nr_users; + char buffer[40]; + uuid_t *uu; + journal_superblock_t *jsb = (journal_superblock_t*)jsb_buffer; + + + fprintf(stdout,"\nJournal Super Block:\n"); + fprintf(stdout," Signature: 0x%08x \n",jsb->s_header.h_magic); + fprintf(stdout," Blocktype : %s \n",type_to_name(jsb->s_header.h_blocktype)); + + fprintf(stdout," Journal block size: %lu \n",jsb->s_blocksize); + fprintf(stdout," Number of journal blocks: %lu \n",jsb->s_maxlen); + fprintf(stdout," Journal block where the journal actually starts: %lu\n",jsb->s_first); + fprintf(stdout," Sequence number of first transaction: %lu\n", jsb->s_sequence); + fprintf(stdout," Journal block of first transaction: %lu\n", jsb->s_start); + fprintf(stdout," Error number: %ld\n",jsb->s_errno); + if ((jsb->s_header.h_blocktype) != JFS_SUPERBLOCK_V2) + return ; +// Remaining fields are only valid in a version-2 superblock + +//FIXME: Strings of Features + fprintf(stdout," Compatible Features: %lu\n",jsb->s_feature_compat); + fprintf(stdout," Incompatible features: %lu\n",jsb->s_feature_incompat); + fprintf(stdout," Read only compatible features: %lu\n",jsb->s_feature_ro_compat); + + uuid_unparse(jsb->s_uuid, buffer); + fprintf(stdout," Journal UUID: %s \n",buffer); + + nr_users = jsb->s_nr_users; + fprintf(stdout," Number of file systems using journal: %lu\n", jsb->s_nr_users); + + fprintf(stdout," Location of superblock copy: %lu\n",jsb->s_dynsuper); + fprintf(stdout," Max journal blocks per transaction: %lu\n",jsb->s_max_transaction); + fprintf(stdout," Max file system blocks per transaction: %lu\n",jsb->s_max_trans_data); + + if (nr_users && (nr_users < 48)) { + fprintf(stdout," IDs of all file systems using the journal:\n"); + for (i = 0; i < nr_users; i++){ + uu=(void*) &jsb->s_users[i*16]; + uuid_unparse(*uu, buffer); + fprintf(stdout,"%02d. : %s\n",i+1,buffer); + } + } + fprintf(stdout," \n\n"); + return; +} + + +// local subfunction +static void journal_superblock_to_cpu ( __u32 *jsb ) +{ + int i; + for (i=0;i<12;i++) + *(jsb+i) = ext2fs_be32_to_cpu(*(jsb+i)); +// UUIDs are endian-independent, so don't swap those bytes + for (i=16;i<20;i++) + *(jsb+i) = ext2fs_be32_to_cpu(*(jsb+i)); + // User IDs are endian-independent +} + + +// open an extract the blocklist from journal +extern int journal_open( char *journal_file_name, int journal_backup_flag ) +{ + char *journal_dev_name = NULL; + int journal_fd = 0; + ext2_ino_t journal_inum; + struct ext2_inode journal_inode; + int retval; + ext2_file_t journal_file; + + pt_buff = NULL; + if (current_fs) jsb_pointer = current_fs->super; + else { + fprintf(stderr,"No filesystem open, this must be a bug\n"); + return(JOURNAL_ERROR); + } + + if (journal_file_name) { + /* Set up to read journal from a regular file somewhere */ + journal_fd = open(journal_file_name, O_RDONLY, 0); + if (journal_fd < 0) { + fprintf(stderr,"Error %d while opening %s for read external journal\n",errno,journal_file_name); + goto errout; + } + + journal_source.where = JOURNAL_IS_EXTERNAL; + journal_source.fd = journal_fd; + journal_source.file = NULL; + printf("Using external Journal at File %s \n",journal_file_name); + + } else if ((journal_inum = jsb_pointer->s_journal_inum)) { + if (journal_backup_flag) { + if (jsb_pointer->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS) { + fprintf(stderr,"no journal backup in super block\n"); + goto errout; + } + memset(&journal_inode, 0, sizeof(struct ext2_inode)); + memcpy(&journal_inode.i_block[0], jsb_pointer->s_jnl_blocks, EXT2_N_BLOCKS*4); + journal_inode.i_size = jsb_pointer->s_jnl_blocks[16]; + journal_inode.i_links_count = 1; + journal_inode.i_mode = LINUX_S_IFREG | 0600; + } else { + if (intern_read_inode(journal_inum, &journal_inode)) + goto errout; + } + + retval = ext2fs_file_open2(current_fs, journal_inum, + &journal_inode, 0, &journal_file); + if (retval) { + fprintf(stderr," Error %d while opening journal\n",retval); + goto errout; + } + journal_source.where = JOURNAL_IS_INTERNAL; + journal_source.file = journal_file; + journal_source.fd = -1 ; + printf("Using internal Journal at Inode %d\n",journal_inum); + + } else { + char uuid[37]; + + uuid_unparse(jsb_pointer->s_journal_uuid, uuid); + journal_dev_name = blkid_get_devname(NULL, "UUID", uuid); + if (!journal_dev_name) + journal_dev_name = blkid_devno_to_devname(jsb_pointer->s_journal_dev); + if (!journal_dev_name) { + fprintf(stderr,"filesystem has no journal\n"); + goto errout; + } + journal_fd = open(journal_dev_name, O_RDONLY, 0); + if (journal_fd < 0) { + fprintf(stderr,"%d while opening %s for external journal\n",errno,journal_dev_name); + free(journal_dev_name); + goto errout; + } + printf("Using external journal found at %s\n", journal_dev_name); + free(journal_dev_name); + journal_source.where = JOURNAL_IS_EXTERNAL; + journal_source.fd = journal_fd; + journal_source.file = NULL; + } + return init_journal(); + +errout: + fprintf(stderr,"Error: while open journal\n" ); + jsb_pointer = NULL; + return (JOURNAL_CLOSE); +} + + +// close the journal (last function in main() if the journal open) +extern int journal_close(void) +{ + jsb_pointer = NULL; + pt_count = 0; + if ( pt_buff ) { + free(pt_buff); + pt_buff = NULL; + } + + if ((journal_source.where == JOURNAL_IS_INTERNAL) && (journal_source.file)){ + ext2fs_file_close(journal_source.file); + journal_source.file = NULL; + } + else if (journal_source.fd > 0) { + close(journal_source.fd); + journal_source.fd = -1; + } + return (JOURNAL_CLOSE); +} + + +//print hexdump of a journalblock +int dump_journal_block( __u32 block_nr , int flag){ + char *buf = NULL; + int got,retval = 0; + int blocksize = current_fs->blocksize; + + buf = (char*) malloc(blocksize); + if(! buf) return 1 ; + + retval = read_journal_block(block_nr * blocksize ,buf,blocksize,&got); + if (retval || got != blocksize) + return retval; + + blockhex (stdout, buf, flag, blocksize); + if (buf) + free(buf); + return 0; +} + + +//read a journal block +int read_journal_block(off_t offset, char *buf, int size, unsigned int *got) +{ + int retval; + + if (journal_source.where == JOURNAL_IS_EXTERNAL) { + if (lseek(journal_source.fd, offset, SEEK_SET) < 0) { + retval = errno; + fprintf(stderr,"Error %d while seeking in reading journal",retval); + return retval; + } + retval = read(journal_source.fd, buf, size); + if (retval >= 0) { + *got = retval; + retval = 0; + } else retval = errno; + } else { + retval = ext2fs_file_lseek(journal_source.file, offset, EXT2_SEEK_SET, NULL); + if (retval) { + fprintf(stderr,"Error %d while seeking in reading journal",retval); + return retval; + } + retval = ext2fs_file_read(journal_source.file, buf, size, got); + } + + if (retval) + fprintf(stderr,"Error %d while reading journal",retval); + else if (*got != (unsigned int) size) { + fprintf(stderr,"short read (read %d, expected %d) while reading journal", *got, size); + retval = -1; + } + return retval; +} + + + +static const char *type_to_name(int btype) +{ + switch (btype) { + case JFS_DESCRIPTOR_BLOCK: + return "descriptor block"; + case JFS_COMMIT_BLOCK: + return "commit block"; + case JFS_SUPERBLOCK_V1: + return "V1 superblock"; + case JFS_SUPERBLOCK_V2: + return "V2 superblock"; + case JFS_REVOKE_BLOCK: + return "revoke table"; + } + return "unrecognised type"; +} + + +//check if block is a inodeblock local function +static int bock_is_inodetable(blk64_t block){ + int group; + struct ext2_group_desc *gdp; + blk64_t last; +//FIXME for struct ext4_group_desc 48/64BIT + for (group = 0; group < current_fs->group_desc_count; group++){ + gdp = ¤t_fs->group_desc[group]; + if (block >= (gdp->bg_inode_table + current_fs->inode_blocks_per_group)) + continue; + if (block >= gdp->bg_inode_table) + return 1; + else +//FIXME: if the last group has a small inodetable + break; + } +return 0; +} + + +// get last timestamp of all inode in this journalblock +static __u32 get_transaction_time( __u32 j_block){ + char *buf; + struct ext2_inode *inode; + __u32 t_time = 0; + int ino, got ,retval = 0; + int inode_size = EXT2_INODE_SIZE(current_fs->super); + int blocksize = current_fs->blocksize; + + buf = (char*) malloc(blocksize); + if(! buf) return 0 ; + + retval = read_journal_block(j_block * blocksize ,buf,blocksize,&got); + if (retval || got != blocksize){ + fprintf(stderr,"Error while read journal block %ld\n",j_block); + t_time = 0; + goto errout; + } + for (ino = 0; ino < EXT2_INODES_PER_BLOCK(current_fs->super);ino++){ + inode = (struct ext2_inode*) (buf + (inode_size * ino)); + if (t_time < ext2fs_le32_to_cpu(inode->i_ctime)) + t_time = ext2fs_le32_to_cpu(inode->i_ctime); + if (t_time < ext2fs_le32_to_cpu(inode->i_atime)) + t_time = ext2fs_le32_to_cpu(inode->i_atime); + + } + +errout: +if (buf) free(buf); +return t_time; +} + + +//print all usable copy of filesystem blocks are found in journal +void print_block_list(int flag){ + journal_descriptor_tag_t *pointer; + __u32 counter; + __u32 transaction_time = 0; + + pointer = pt_buff; + printf("\nFound %d copy of Filesystemblock in Journal\n",pt_count); + printf("FS-Block Journal Transact %s\n",(flag) ? "Time in sec Time of Transaction" :"" ); + + for (counter=0 ; counter<pt_count ; counter++) { + if (flag){ + if (bock_is_inodetable (pointer->f_blocknr)){ + transaction_time = get_transaction_time(pointer->j_blocknr); + } + else transaction_time = 0; + } + if (transaction_time) + fprintf(stdout,"%12llu %8lu %8lu %8lu %s",(__u64) pointer->f_blocknr , pointer->j_blocknr , + pointer->transaction, transaction_time, time_to_string(transaction_time) ); + else + fprintf(stdout,"%12llu %8lu %8lu\n",(__u64) pointer->f_blocknr , pointer->j_blocknr , + pointer->transaction); + pointer++ ; + } +} + + +//print all transactions for a fs-block +void print_block_transaction(blk64_t block_nr, int flag){ + journal_descriptor_tag_t *pointer; + __u32 counter; + int is_inode_block = 0; + __u32 transaction_time = 0; + + if (flag) is_inode_block = bock_is_inodetable(block_nr); + + pointer = pt_buff; + printf("\nTransactions of Filesystemblock %llu in Journal\n",block_nr); + printf("FS-Block Journal Transact %s\n",(is_inode_block) ? "Time in sec Time of Transaction" :""); + for (counter=0 ; counter<pt_count ; counter++) { + if (pointer->f_blocknr == block_nr){ + if (is_inode_block){ + transaction_time = get_transaction_time(pointer->j_blocknr); + fprintf(stdout,"%12llu %8lu %8lu %8lu %s",(__u64) pointer->f_blocknr , pointer->j_blocknr , + pointer->transaction, transaction_time, time_to_string(transaction_time) ); + }else + fprintf(stdout,"%12llu %8lu %8lu\n",(__u64) pointer->f_blocknr , pointer->j_blocknr , pointer->transaction); + } + pointer++ ; + } +} + + + +//get the journalblocknumber for a transactionnumber +__u32 get_journal_blocknr(blk64_t block_nr, __u32 trans_nr){ + __u32 journal_nr = 0; + journal_descriptor_tag_t *pointer; + int i; + pointer = pt_buff; + for (i=0; i<pt_count;i++ , pointer++){ + if ( pointer->transaction != trans_nr) continue; + if ( pointer->f_blocknr == block_nr ) journal_nr = pointer->j_blocknr ; + } +return journal_nr; +} + + +// get the last dir-block for transaction from journal or if not found the real block +//FIXME: blk64_t ???? +int get_last_block(char *buf, blk_t *block, __u32 t_start, __u32 t_end){ + int retval = 0; + int i , count , got; + char *journal_tag_buf = NULL; + journal_descriptor_tag_t *block_list; + blk_t j_block = 0; + int blksize = current_fs->blocksize; + + if ((!t_start) && (!t_end)){ +//there is no transaction, it is not from a journal Inode + return io_channel_read_blk(current_fs->io, *block, 1, buf); + } + + count = get_block_list_count((blk64_t)*block) ; + + journal_tag_buf = (void *)malloc((sizeof(journal_descriptor_tag_t) * count)); + if (!journal_tag_buf) { +// fprintf(stderr,"Error: while allocate Memory for blocklist\n"); + retval = BLOCK_ABORT; + goto errout; + } + block_list = (journal_descriptor_tag_t *) journal_tag_buf; + count = get_block_list(block_list, *block, count); + + for (i = 0 ; i < count ; i++ , block_list++ ) { + if (block_list->transaction < t_start ) + continue; + if (block_list->transaction <= t_end){ + j_block = block_list->j_blocknr; + if (i != (count -1)) + continue; + } + + retval = read_journal_block((j_block) ? (j_block * blksize) : (block_list->j_blocknr * blksize), + buf , current_fs->blocksize , &got); + if (retval) { + retval = BLOCK_ERROR ; + } + goto errout; + } + +// if not in journal found, use real block + retval = io_channel_read_blk(current_fs->io, *block, 1, buf); + + +errout: + if (journal_tag_buf) free(journal_tag_buf); + +return retval; +} + + + //get count of journal blocklist +int get_block_list_count(blk64_t block){ + int counter = 0; + int i; + journal_descriptor_tag_t *list_pointer = pt_buff ; + + for (i=0;i<pt_count;i++,list_pointer++) + if (list_pointer->f_blocknr == block) counter++ ; +return counter; +} + + +//local sortfunction for journalblocks +static int sort_block_list(journal_descriptor_tag_t *pointer, int counter ){ + journal_descriptor_tag_t p1; + int c1,c2,flag=1; + c1 = 0; +//FIXME : for the faster double-sided Bubblesort + while (flag) { + flag = 0; + for (c2 = 0;c2 < (counter -c1 -1);c2++){ + if ((pointer+c2)->transaction > (pointer+c2+1)->transaction ){ + p1=*(pointer+c2); + *(pointer+c2)=*(pointer+c2+1); + *(pointer+c2+1)=p1; + flag = 1 ; + } + } + c1++ ; + } +return counter; +} + + +//get a sorted list of all copys of a filesystemblock +int get_block_list(journal_descriptor_tag_t *pointer, blk64_t block , int counter ){ + int i,j = 0; + journal_descriptor_tag_t *list_pointer = pt_buff ; + journal_descriptor_tag_t *fill_pointer = pointer ; + + if (counter) { + for (i=0,j=0;(i<pt_count && j<counter);i++,list_pointer++) + if (list_pointer->f_blocknr == block) { + fill_pointer->f_blocknr = block; + fill_pointer->j_blocknr = list_pointer->j_blocknr; + fill_pointer->transaction = list_pointer->transaction; + fill_pointer++ ; + j++; + } + sort_block_list(pointer,counter); + + } + return j; +} + +//extract the journal in the local intern blocklist +static void extract_descriptor_block(char *buf, journal_superblock_t *jsb, + unsigned int *blockp, int blocksize, + tid_t transaction, unsigned int *wrapflag ) +{ + int offset, tag_size = JBD_TAG_SIZE32; + char *tagp; + journal_block_tag_t *tag; + unsigned int blocknr; + __u32 tag_block; + __u32 tag_flags; + + if (jsb->s_feature_incompat & JFS_FEATURE_INCOMPAT_64BIT) + tag_size = JBD_TAG_SIZE64; + + offset = sizeof(journal_header_t); + blocknr = *blockp; +#ifdef DEBUG + fprintf(stderr, "Dumping descriptor block, sequence %u, at block %u:\n", transaction, blocknr); +#endif + ++blocknr; + if (blocknr >= jsb->s_maxlen) { + if (*wrapflag == WRAP_ON) { + blocknr -= (jsb->s_maxlen - jsb->s_first); + *wrapflag = WRAP_READY; + } + else { + *blockp = blocknr; + return; + } + } + + do { + /* Work out the location of the current tag, and skip to + * the next one... */ + tagp = &buf[offset]; + tag = (journal_block_tag_t *) tagp; + offset += tag_size; + + /* ... and if we have gone too far, then we've reached the + end of this block. */ + if (offset > blocksize) break; + + tag_block = be32_to_cpu(tag->t_blocknr) ; + tag_flags = be32_to_cpu(tag->t_flags); + + if (!(tag_flags & JFS_FLAG_SAME_UUID)) + offset += 16; + +#ifdef DEBUG + fprintf(stderr, " FS block %12lu logged at ", tag_block); + fprintf(stderr, "sequence %u, ", transaction); + fprintf(stderr, "journal block %u (flags 0x%x)\n", blocknr,tag_flags); +#endif + pt->f_blocknr = tag_block ; + if (tag_size > JBD_TAG_SIZE32) pt->f_blocknr |= (__u64)be32_to_cpu(tag->t_blocknr_high) << 32; + pt->j_blocknr = blocknr; + pt->transaction = transaction; + pt++; + pt_count++; + + ++blocknr; + if (blocknr >= jsb->s_maxlen) { + if (*wrapflag == WRAP_ON) { + blocknr -= (jsb->s_maxlen - jsb->s_first); + *wrapflag = WRAP_READY; + } + else { + *blockp = blocknr; + return; + } + } + + } while (!(tag_flags & JFS_FLAG_LAST_TAG)); + *blockp = (*wrapflag == WRAP_READY) ? jsb->s_maxlen : blocknr ; +} + + + +// init and extract the journal in the local private data +static int init_journal(void) +{ + struct ext2_super_block *sb; + char buf[8192]; + journal_superblock_t *jsb; + unsigned int blocksize = 1024; + unsigned int got; + int retval; + __u32 magic, sequence, blocktype; + journal_header_t *header; + + tid_t transaction; + unsigned int blocknr = 0; + unsigned int maxlen = 0; + unsigned int wrapflag; + + /* First, check to see if there's an ext2 superblock header */ + retval = read_journal_block( 0, buf, 2048, &got); + if (retval) goto errout; + + jsb = (journal_superblock_t *) buf; + sb = (struct ext2_super_block *) (buf+1024); +#ifdef WORDS_BIGENDIAN + if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ext2fs_swap_super(sb); +#endif + + if ((be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) && + (sb->s_magic == EXT2_SUPER_MAGIC) && + (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { + blocksize = EXT2_BLOCK_SIZE(sb); + blocknr = (blocksize == 1024) ? 2 : 1; + uuid_unparse(sb->s_uuid, jsb_buffer); +#ifdef DEBUG + fprintf(stderr, "Ext2 superblock header found."); + fprintf(stderr, "\tuuid=%s", jsb_buffer); + fprintf(stderr, "\tblocksize=%d", blocksize); + fprintf(stderr, "\tjournal data size %lu\n",(unsigned long) sb->s_blocks_count); +#endif + } + + /* Next, read the journal superblock */ + retval = read_journal_block(blocknr*blocksize,jsb_buffer, 1024, &got); + if (retval) goto errout; + + jsb = (journal_superblock_t *) jsb_buffer; + if (be32_to_cpu(jsb->s_header.h_magic) != JFS_MAGIC_NUMBER) { + fprintf(stderr, "Journal superblock magic number invalid!\n"); + return JOURNAL_ERROR ; + } + journal_superblock_to_cpu((__u32*)jsb_buffer); + + blocksize = jsb->s_blocksize; + transaction = jsb->s_sequence; + maxlen = jsb->s_maxlen; + blocknr = jsb->s_first; + wrapflag = WRAP_UNDEF ; + + + pt_buff = malloc(maxlen * sizeof(journal_descriptor_tag_t)); + pt = pt_buff ; + pt_count = 0; + if (pt_buff == NULL) { + fprintf(stderr,"Error: can't allocate %d Memory\n",maxlen * sizeof(journal_descriptor_tag_t)); + goto errout; + } + +#ifdef DEBUG + fprintf(stderr, "Journal starts at block %u, transaction %u\n", blocknr, transaction); +#endif + while ( blocknr < maxlen ){ + retval = read_journal_block(blocknr*blocksize, buf, blocksize, &got); + if (retval || got != blocksize) + //return JOURNAL_OPEN; + goto errout; + + header = (journal_header_t *) buf; + + magic = be32_to_cpu(header->h_magic); + sequence = be32_to_cpu(header->h_sequence); + blocktype = be32_to_cpu(header->h_blocktype); + + if (magic != JFS_MAGIC_NUMBER) { +#ifdef DEBUG + fprintf (stderr, "No magic number at block %u: skip this block .\n", blocknr); +#endif + if ( ! wrapflag ) wrapflag = WRAP_ON ; + blocknr++ ; + continue; + } +#ifdef DEBUG + fprintf (stderr, "Found expected sequence %u, type %u (%s) at block %u\n", + sequence, blocktype, type_to_name(blocktype), blocknr); +#endif + if ( ! wrapflag ) wrapflag = WRAP_OFF ; + + switch (blocktype) { + case JFS_DESCRIPTOR_BLOCK: + extract_descriptor_block(buf, jsb, &blocknr, blocksize, sequence, &wrapflag); + continue; + + case JFS_COMMIT_BLOCK: + blocknr++; + continue; + + case JFS_REVOKE_BLOCK: + blocknr++; + continue; + + default: + fprintf (stderr, "Unexpected block type %u at block %u.\n", blocktype, blocknr); + return JOURNAL_OPEN; + } + } +return JOURNAL_OPEN ; + +errout: + fprintf(stderr,"Error: can't read journal\n"); + if ( pt_buff ) { + free(pt_buff); + pt_buff = NULL; + } + return JOURNAL_ERROR ; + +} + + diff --git a/src/journal.h b/src/journal.h new file mode 100644 index 0000000..34ada1a --- /dev/null +++ b/src/journal.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef JOURNAL_H +#define JOURNAL_H + + +//struct for cache the descriptor data +typedef struct journal_descriptor_tag_s +{ + blk64_t f_blocknr; /* Tho od-disk block number */ + __u32 j_blocknr; /* The on-journal block number */ + __u32 transaction; /* Transaction Sequence*/ +} journal_descriptor_tag_t; + + +void dump_journal_superblock( void); //print journal superblock +extern int journal_open(char* , int );// open an extract the blocklist from journal +extern int journal_close(void); // close the journal (last function in main() if the journal open) +static int init_journal(void); // main for extract the journal to the local private data +static const char *type_to_name(int); +int get_block_list_count(blk64_t block);//get count of journal blocklist +int get_block_list(journal_descriptor_tag_t *, blk64_t, int);//get a sortet list of all copys of a filesystemblock +int read_journal_block(off_t, char*, int, unsigned int*); +void print_block_list(int); +int dump_journal_block( __u32 , int ); +void print_block_transaction(blk64_t,int);//print all transactions for a fs-block +__u32 get_journal_blocknr(blk64_t, __u32);// get the last dir-block for transaction from journal or if not found the real block + + +#endif
\ No newline at end of file diff --git a/src/kernel-jbd.h b/src/kernel-jbd.h new file mode 100644 index 0000000..331b0e4 --- /dev/null +++ b/src/kernel-jbd.h @@ -0,0 +1,951 @@ +/* + * linux/include/linux/jbd.h + * + * Written by Stephen C. Tweedie <sct@redhat.com> + * + * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved + * + * This file is part of the Linux kernel and is made available under + * the terms of the GNU General Public License, version 2, or at your + * option, any later version, incorporated herein by reference. + * + * Definitions for transaction data structures for the buffer cache + * filesystem journaling support. + */ + +#ifndef _LINUX_JBD_H +#define _LINUX_JBD_H + +#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__) + +/* Allow this file to be included directly into e2fsprogs */ +#ifndef __KERNEL__ +#include "jfs_compat.h" +#define JFS_DEBUG +#define jfs_debug jbd_debug +#else + +#include <linux/journal-head.h> +#include <linux/stddef.h> +#include <asm/semaphore.h> +#endif + +#ifndef __GNUC__ +#define __FUNCTION__ "" +#endif + +#define journal_oom_retry 1 + +#ifdef __STDC__ +#ifdef CONFIG_JBD_DEBUG +/* + * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal + * consistency checks. By default we don't do this unless + * CONFIG_JBD_DEBUG is on. + */ +#define JBD_EXPENSIVE_CHECKING +extern int journal_enable_debug; + +#define jbd_debug(n, f, a...) \ + do { \ + if ((n) <= journal_enable_debug) { \ + printk (KERN_DEBUG "(%s, %d): %s: ", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } \ + } while (0) +#else +#ifdef __GNUC__ +#define jbd_debug(f, a...) /**/ +#else +#define jbd_debug(f, ...) /**/ +#endif +#endif +#else +#define jbd_debug(x) /* AIX doesn't do STDC */ +#endif + +extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry); +#define jbd_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry) +#define jbd_rep_kmalloc(size, flags) \ + __jbd_kmalloc(__FUNCTION__, (size), (flags), 1) + +#define JFS_MIN_JOURNAL_BLOCKS 1024 + +#ifdef __KERNEL__ +typedef struct handle_s handle_t; /* Atomic operation type */ +typedef struct journal_s journal_t; /* Journal control structure */ +#endif + +/* + * Internal structures used by the logging mechanism: + */ + +#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ + +/* + * On-disk structures + */ + +/* + * Descriptor block types: + */ + +#define JFS_DESCRIPTOR_BLOCK 1 +#define JFS_COMMIT_BLOCK 2 +#define JFS_SUPERBLOCK_V1 3 +#define JFS_SUPERBLOCK_V2 4 +#define JFS_REVOKE_BLOCK 5 + +/* + * Standard header for all descriptor blocks: + */ +typedef struct journal_header_s +{ + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; +} journal_header_t; + +/* + * Checksum types. + */ +#define JBD2_CRC32_CHKSUM 1 +#define JBD2_MD5_CHKSUM 2 +#define JBD2_SHA1_CHKSUM 3 + +#define JBD2_CRC32_CHKSUM_SIZE 4 + +#define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32)) +/* + * Commit block header for storing transactional checksums: + */ +struct commit_header { + __u32 h_magic; + __u32 h_blocktype; + __u32 h_sequence; + unsigned char h_chksum_type; + unsigned char h_chksum_size; + unsigned char h_padding[2]; + __u32 h_chksum[JBD2_CHECKSUM_BYTES]; + __u64 h_commit_sec; + __u32 h_commit_nsec; +}; + +/* + * The block tag: used to describe a single buffer in the journal + */ +typedef struct journal_block_tag_s +{ + __u32 t_blocknr; /* The on-disk block number */ + __u32 t_flags; /* See below */ + __u32 t_blocknr_high; /* most-significant high 32bits. */ +} journal_block_tag_t; + +#define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t)) +#define JBD_TAG_SIZE32 (8) + +/* + * The revoke descriptor: used on disk to describe a series of blocks to + * be revoked from the log + */ +typedef struct journal_revoke_header_s +{ + journal_header_t r_header; + int r_count; /* Count of bytes used in the block */ +} journal_revoke_header_t; + + +/* Definitions for the journal tag flags word: */ +#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ +#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ +#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ +#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ + + +/* + * The journal superblock. All fields are in big-endian byte order. + */ +typedef struct journal_superblock_s +{ +/* 0x0000 */ + journal_header_t s_header; + +/* 0x000C */ + /* Static information describing the journal */ + __u32 s_blocksize; /* journal device blocksize */ + __u32 s_maxlen; /* total blocks in journal file */ + __u32 s_first; /* first block of log information */ + +/* 0x0018 */ + /* Dynamic information describing the current state of the log */ + __u32 s_sequence; /* first commit ID expected in log */ + __u32 s_start; /* blocknr of start of log */ + +/* 0x0020 */ + /* Error value, as set by journal_abort(). */ + __s32 s_errno; + +/* 0x0024 */ + /* Remaining fields are only valid in a version-2 superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ +/* 0x0030 */ + __u8 s_uuid[16]; /* 128-bit uuid for journal */ + +/* 0x0040 */ + __u32 s_nr_users; /* Nr of filesystems sharing log */ + + __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ + +/* 0x0048 */ + __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ + __u32 s_max_trans_data; /* Limit of data blocks per trans. */ + +/* 0x0050 */ + __u32 s_padding[44]; + +/* 0x0100 */ + __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ +/* 0x0400 */ +} journal_superblock_t; + +#define JFS_HAS_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) +#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) +#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ + ((j)->j_format_version >= 2 && \ + ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) + +#define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 + +#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 +#define JFS_FEATURE_INCOMPAT_64BIT 0x00000002 +#define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 + +/* Features known to this kernel version: */ +#define JFS_KNOWN_COMPAT_FEATURES 0 +#define JFS_KNOWN_ROCOMPAT_FEATURES 0 +#define JFS_KNOWN_INCOMPAT_FEATURES (JFS_FEATURE_INCOMPAT_REVOKE|\ + JFS_FEATURE_INCOMPAT_ASYNC_COMMIT) + +#ifdef __KERNEL__ + +#include <linux/fs.h> +#include <linux/sched.h> + +#define JBD_ASSERTIONS +#ifdef JBD_ASSERTIONS +#define J_ASSERT(assert) \ +do { \ + if (!(assert)) { \ + printk (KERN_EMERG \ + "Assertion failure in %s() at %s:%d: \"%s\"\n", \ + __FUNCTION__, __FILE__, __LINE__, # assert); \ + BUG(); \ + } \ +} while (0) + +#if defined(CONFIG_BUFFER_DEBUG) +void buffer_assertion_failure(struct buffer_head *bh); +#define J_ASSERT_BH(bh, expr) \ + do { \ + if (!(expr)) \ + buffer_assertion_failure(bh); \ + J_ASSERT(expr); \ + } while (0) +#define J_ASSERT_JH(jh, expr) J_ASSERT_BH(jh2bh(jh), expr) +#else +#define J_ASSERT_BH(bh, expr) J_ASSERT(expr) +#define J_ASSERT_JH(jh, expr) J_ASSERT(expr) +#endif + +#else +#define J_ASSERT(assert) +#endif /* JBD_ASSERTIONS */ + +enum jbd_state_bits { + BH_JWrite + = BH_PrivateStart, /* 1 if being written to log (@@@ DEBUGGING) */ + BH_Freed, /* 1 if buffer has been freed (truncated) */ + BH_Revoked, /* 1 if buffer has been revoked from the log */ + BH_RevokeValid, /* 1 if buffer revoked flag is valid */ + BH_JBDDirty, /* 1 if buffer is dirty but journaled */ +}; + +/* Return true if the buffer is one which JBD is managing */ +static inline int buffer_jbd(struct buffer_head *bh) +{ + return __buffer_state(bh, JBD); +} + +static inline struct buffer_head *jh2bh(struct journal_head *jh) +{ + return jh->b_bh; +} + +static inline struct journal_head *bh2jh(struct buffer_head *bh) +{ + return bh->b_private; +} + +struct jbd_revoke_table_s; + +/* The handle_t type represents a single atomic update being performed + * by some process. All filesystem modifications made by the process go + * through this handle. Recursive operations (such as quota operations) + * are gathered into a single update. + * + * The buffer credits field is used to account for journaled buffers + * being modified by the running process. To ensure that there is + * enough log space for all outstanding operations, we need to limit the + * number of outstanding buffers possible at any time. When the + * operation completes, any buffer credits not used are credited back to + * the transaction, so that at all times we know how many buffers the + * outstanding updates on a transaction might possibly touch. */ + +struct handle_s +{ + /* Which compound transaction is this update a part of? */ + transaction_t * h_transaction; + + /* Number of remaining buffers we are allowed to dirty: */ + int h_buffer_credits; + + /* Reference count on this handle */ + int h_ref; + + /* Field for caller's use to track errors through large fs + operations */ + int h_err; + + /* Flags */ + unsigned int h_sync: 1; /* sync-on-close */ + unsigned int h_jdata: 1; /* force data journaling */ + unsigned int h_aborted: 1; /* fatal error on handle */ +}; + + +/* The transaction_t type is the guts of the journaling mechanism. It + * tracks a compound transaction through its various states: + * + * RUNNING: accepting new updates + * LOCKED: Updates still running but we don't accept new ones + * RUNDOWN: Updates are tidying up but have finished requesting + * new buffers to modify (state not used for now) + * FLUSH: All updates complete, but we are still writing to disk + * COMMIT: All data on disk, writing commit record + * FINISHED: We still have to keep the transaction for checkpointing. + * + * The transaction keeps track of all of the buffers modified by a + * running transaction, and all of the buffers committed but not yet + * flushed to home for finished transactions. + */ + +struct transaction_s +{ + /* Pointer to the journal for this transaction. */ + journal_t * t_journal; + + /* Sequence number for this transaction */ + tid_t t_tid; + + /* Transaction's current state */ + enum { + T_RUNNING, + T_LOCKED, + T_RUNDOWN, + T_FLUSH, + T_COMMIT, + T_FINISHED + } t_state; + + /* Where in the log does this transaction's commit start? */ + unsigned long t_log_start; + + /* Doubly-linked circular list of all inodes owned by this + transaction */ /* AKPM: unused */ + struct inode * t_ilist; + + /* Number of buffers on the t_buffers list */ + int t_nr_buffers; + + /* Doubly-linked circular list of all buffers reserved but not + yet modified by this transaction */ + struct journal_head * t_reserved_list; + + /* Doubly-linked circular list of all metadata buffers owned by this + transaction */ + struct journal_head * t_buffers; + + /* + * Doubly-linked circular list of all data buffers still to be + * flushed before this transaction can be committed. + * Protected by journal_datalist_lock. + */ + struct journal_head * t_sync_datalist; + + /* + * Doubly-linked circular list of all writepage data buffers + * still to be written before this transaction can be committed. + * Protected by journal_datalist_lock. + */ + struct journal_head * t_async_datalist; + + /* Doubly-linked circular list of all forget buffers (superceded + buffers which we can un-checkpoint once this transaction + commits) */ + struct journal_head * t_forget; + + /* + * Doubly-linked circular list of all buffers still to be + * flushed before this transaction can be checkpointed. + */ + /* Protected by journal_datalist_lock */ + struct journal_head * t_checkpoint_list; + + /* Doubly-linked circular list of temporary buffers currently + undergoing IO in the log */ + struct journal_head * t_iobuf_list; + + /* Doubly-linked circular list of metadata buffers being + shadowed by log IO. The IO buffers on the iobuf list and the + shadow buffers on this list match each other one for one at + all times. */ + struct journal_head * t_shadow_list; + + /* Doubly-linked circular list of control buffers being written + to the log. */ + struct journal_head * t_log_list; + + /* Number of outstanding updates running on this transaction */ + int t_updates; + + /* Number of buffers reserved for use by all handles in this + * transaction handle but not yet modified. */ + int t_outstanding_credits; + + /* + * Forward and backward links for the circular list of all + * transactions awaiting checkpoint. + */ + /* Protected by journal_datalist_lock */ + transaction_t *t_cpnext, *t_cpprev; + + /* When will the transaction expire (become due for commit), in + * jiffies ? */ + unsigned long t_expires; + + /* How many handles used this transaction? */ + int t_handle_count; +}; + + +/* The journal_t maintains all of the journaling state information for a + * single filesystem. It is linked to from the fs superblock structure. + * + * We use the journal_t to keep track of all outstanding transaction + * activity on the filesystem, and to manage the state of the log + * writing process. */ + +struct journal_s +{ + /* General journaling state flags */ + unsigned long j_flags; + + /* Is there an outstanding uncleared error on the journal (from + * a prior abort)? */ + int j_errno; + + /* The superblock buffer */ + struct buffer_head * j_sb_buffer; + journal_superblock_t * j_superblock; + + /* Version of the superblock format */ + int j_format_version; + + /* Number of processes waiting to create a barrier lock */ + int j_barrier_count; + + /* The barrier lock itself */ + struct semaphore j_barrier; + + /* Transactions: The current running transaction... */ + transaction_t * j_running_transaction; + + /* ... the transaction we are pushing to disk ... */ + transaction_t * j_committing_transaction; + + /* ... and a linked circular list of all transactions waiting + * for checkpointing. */ + /* Protected by journal_datalist_lock */ + transaction_t * j_checkpoint_transactions; + + /* Wait queue for waiting for a locked transaction to start + committing, or for a barrier lock to be released */ + wait_queue_head_t j_wait_transaction_locked; + + /* Wait queue for waiting for checkpointing to complete */ + wait_queue_head_t j_wait_logspace; + + /* Wait queue for waiting for commit to complete */ + wait_queue_head_t j_wait_done_commit; + + /* Wait queue to trigger checkpointing */ + wait_queue_head_t j_wait_checkpoint; + + /* Wait queue to trigger commit */ + wait_queue_head_t j_wait_commit; + + /* Wait queue to wait for updates to complete */ + wait_queue_head_t j_wait_updates; + + /* Semaphore for locking against concurrent checkpoints */ + struct semaphore j_checkpoint_sem; + + /* The main journal lock, used by lock_journal() */ + struct semaphore j_sem; + + /* Journal head: identifies the first unused block in the journal. */ + unsigned long j_head; + + /* Journal tail: identifies the oldest still-used block in the + * journal. */ + unsigned long j_tail; + + /* Journal free: how many free blocks are there in the journal? */ + unsigned long j_free; + + /* Journal start and end: the block numbers of the first usable + * block and one beyond the last usable block in the journal. */ + unsigned long j_first, j_last; + + /* Device, blocksize and starting block offset for the location + * where we store the journal. */ + kdev_t j_dev; + int j_blocksize; + unsigned int j_blk_offset; + + /* Device which holds the client fs. For internal journal this + * will be equal to j_dev. */ + kdev_t j_fs_dev; + + /* Total maximum capacity of the journal region on disk. */ + unsigned int j_maxlen; + + /* Optional inode where we store the journal. If present, all + * journal block numbers are mapped into this inode via + * bmap(). */ + struct inode * j_inode; + + /* Sequence number of the oldest transaction in the log */ + tid_t j_tail_sequence; + /* Sequence number of the next transaction to grant */ + tid_t j_transaction_sequence; + /* Sequence number of the most recently committed transaction */ + tid_t j_commit_sequence; + /* Sequence number of the most recent transaction wanting commit */ + tid_t j_commit_request; + + /* Journal uuid: identifies the object (filesystem, LVM volume + * etc) backed by this journal. This will eventually be + * replaced by an array of uuids, allowing us to index multiple + * devices within a single journal and to perform atomic updates + * across them. */ + + __u8 j_uuid[16]; + + /* Pointer to the current commit thread for this journal */ + struct task_struct * j_task; + + /* Maximum number of metadata buffers to allow in a single + * compound commit transaction */ + int j_max_transaction_buffers; + + /* What is the maximum transaction lifetime before we begin a + * commit? */ + unsigned long j_commit_interval; + + /* The timer used to wakeup the commit thread: */ + struct timer_list * j_commit_timer; + int j_commit_timer_active; + + /* Link all journals together - system-wide */ + struct list_head j_all_journals; + + /* The revoke table: maintains the list of revoked blocks in the + current transaction. */ + struct jbd_revoke_table_s *j_revoke; + + /* Failed journal commit ID */ + unsigned int j_failed_commit; +}; + +/* + * Journal flag definitions + */ +#define JFS_UNMOUNT 0x001 /* Journal thread is being destroyed */ +#define JFS_ABORT 0x002 /* Journaling has been aborted for errors. */ +#define JFS_ACK_ERR 0x004 /* The errno in the sb has been acked */ +#define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */ +#define JFS_LOADED 0x010 /* The journal superblock has been loaded */ + +/* + * Function declarations for the journaling transaction and buffer + * management + */ + +/* Filing buffers */ +extern void __journal_unfile_buffer(struct journal_head *); +extern void journal_unfile_buffer(struct journal_head *); +extern void __journal_refile_buffer(struct journal_head *); +extern void journal_refile_buffer(struct journal_head *); +extern void __journal_file_buffer(struct journal_head *, transaction_t *, int); +extern void __journal_free_buffer(struct journal_head *bh); +extern void journal_file_buffer(struct journal_head *, transaction_t *, int); +extern void __journal_clean_data_list(transaction_t *transaction); + +/* Log buffer allocation */ +extern struct journal_head * journal_get_descriptor_buffer(journal_t *); +extern unsigned long journal_next_log_block(journal_t *); + +/* Commit management */ +extern void journal_commit_transaction(journal_t *); + +/* Checkpoint list management */ +int __journal_clean_checkpoint_list(journal_t *journal); +extern void journal_remove_checkpoint(struct journal_head *); +extern void __journal_remove_checkpoint(struct journal_head *); +extern void journal_insert_checkpoint(struct journal_head *, transaction_t *); +extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *); + +/* Buffer IO */ +extern int +journal_write_metadata_buffer(transaction_t *transaction, + struct journal_head *jh_in, + struct journal_head **jh_out, + int blocknr); + +/* Transaction locking */ +extern void __wait_on_journal (journal_t *); + +/* + * Journal locking. + * + * We need to lock the journal during transaction state changes so that + * nobody ever tries to take a handle on the running transaction while + * we are in the middle of moving it to the commit phase. + * + * Note that the locking is completely interrupt unsafe. We never touch + * journal structures from interrupts. + * + * In 2.2, the BKL was required for lock_journal. This is no longer + * the case. + */ + +static inline void lock_journal(journal_t *journal) +{ + down(&journal->j_sem); +} + +/* This returns zero if we acquired the semaphore */ +static inline int try_lock_journal(journal_t * journal) +{ + return down_trylock(&journal->j_sem); +} + +static inline void unlock_journal(journal_t * journal) +{ + up(&journal->j_sem); +} + + +static inline handle_t *journal_current_handle(void) +{ + return current->journal_info; +} + +/* The journaling code user interface: + * + * Create and destroy handles + * Register buffer modifications against the current transaction. + */ + +extern handle_t *journal_start(journal_t *, int nblocks); +extern handle_t *journal_try_start(journal_t *, int nblocks); +extern int journal_restart (handle_t *, int nblocks); +extern int journal_extend (handle_t *, int nblocks); +extern int journal_get_write_access (handle_t *, struct buffer_head *); +extern int journal_get_create_access (handle_t *, struct buffer_head *); +extern int journal_get_undo_access (handle_t *, struct buffer_head *); +extern int journal_dirty_data (handle_t *, + struct buffer_head *, int async); +extern int journal_dirty_metadata (handle_t *, struct buffer_head *); +extern void journal_release_buffer (handle_t *, struct buffer_head *); +extern void journal_forget (handle_t *, struct buffer_head *); +extern void journal_sync_buffer (struct buffer_head *); +extern int journal_flushpage(journal_t *, struct page *, unsigned long); +extern int journal_try_to_free_buffers(journal_t *, struct page *, int); +extern int journal_stop(handle_t *); +extern int journal_flush (journal_t *); + +extern void journal_lock_updates (journal_t *); +extern void journal_unlock_updates (journal_t *); + +extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev, + int start, int len, int bsize); +extern journal_t * journal_init_inode (struct inode *); +extern int journal_update_format (journal_t *); +extern int journal_check_used_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_check_available_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_set_features + (journal_t *, unsigned long, unsigned long, unsigned long); +extern int journal_create (journal_t *); +extern int journal_load (journal_t *journal); +extern void journal_destroy (journal_t *); +extern int journal_recover (journal_t *journal); +extern int journal_wipe (journal_t *, int); +extern int journal_skip_recovery (journal_t *); +extern void journal_update_superblock (journal_t *, int); +extern void __journal_abort (journal_t *); +extern void journal_abort (journal_t *, int); +extern int journal_errno (journal_t *); +extern void journal_ack_err (journal_t *); +extern int journal_clear_err (journal_t *); +extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr); +extern int journal_force_commit(journal_t *journal); + +/* + * journal_head management + */ +extern struct journal_head + *journal_add_journal_head(struct buffer_head *bh); +extern void journal_remove_journal_head(struct buffer_head *bh); +extern void __journal_remove_journal_head(struct buffer_head *bh); +extern void journal_unlock_journal_head(struct journal_head *jh); + +/* Primary revoke support */ +#define JOURNAL_REVOKE_DEFAULT_HASH 256 +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +extern void journal_destroy_revoke(journal_t *); +extern int journal_revoke (handle_t *, + unsigned long, struct buffer_head *); +extern int journal_cancel_revoke(handle_t *, struct journal_head *); +extern void journal_write_revoke_records(journal_t *, transaction_t *); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +/* The log thread user interface: + * + * Request space in the current transaction, and force transaction commit + * transitions on demand. + */ + +extern int log_space_left (journal_t *); /* Called with journal locked */ +extern tid_t log_start_commit (journal_t *, transaction_t *); +extern void log_wait_commit (journal_t *, tid_t); +extern int log_do_checkpoint (journal_t *, int); + +extern void log_wait_for_space(journal_t *, int nblocks); +extern void __journal_drop_transaction(journal_t *, transaction_t *); +extern int cleanup_journal_tail(journal_t *); + +/* Reduce journal memory usage by flushing */ +extern void shrink_journal_memory(void); + +/* Debugging code only: */ + +#define jbd_ENOSYS() \ +do { \ + printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \ + current->state = TASK_UNINTERRUPTIBLE; \ + schedule(); \ +} while (1) + +/* + * is_journal_abort + * + * Simple test wrapper function to test the JFS_ABORT state flag. This + * bit, when set, indicates that we have had a fatal error somewhere, + * either inside the journaling layer or indicated to us by the client + * (eg. ext3), and that we and should not commit any further + * transactions. + */ + +static inline int is_journal_aborted(journal_t *journal) +{ + return journal->j_flags & JFS_ABORT; +} + +static inline int is_handle_aborted(handle_t *handle) +{ + if (handle->h_aborted) + return 1; + return is_journal_aborted(handle->h_transaction->t_journal); +} + +static inline void journal_abort_handle(handle_t *handle) +{ + handle->h_aborted = 1; +} + +/* Not all architectures define BUG() */ +#ifndef BUG +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ + * ((char *) 0) = 0; \ + } while (0) +#endif /* BUG */ + +#else + +extern int journal_recover (journal_t *journal); +extern int journal_skip_recovery (journal_t *); + +/* Primary revoke support */ +extern int journal_init_revoke(journal_t *, int); +extern void journal_destroy_revoke_caches(void); +extern int journal_init_revoke_caches(void); + +/* Recovery revoke support */ +extern int journal_set_revoke(journal_t *, unsigned long, tid_t); +extern int journal_test_revoke(journal_t *, unsigned long, tid_t); +extern void journal_clear_revoke(journal_t *); +extern void journal_brelse_array(struct buffer_head *b[], int n); + +extern void journal_destroy_revoke(journal_t *); +#endif /* __KERNEL__ */ + +static inline int tid_gt(tid_t x, tid_t y) EXT2FS_ATTR((unused)); +static inline int tid_geq(tid_t x, tid_t y) EXT2FS_ATTR((unused)); + +/* Comparison functions for transaction IDs: perform comparisons using + * modulo arithmetic so that they work over sequence number wraps. */ + +static inline int tid_gt(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference > 0); +} + +static inline int tid_geq(tid_t x, tid_t y) +{ + int difference = (x - y); + return (difference >= 0); +} + +extern int journal_blocks_per_page(struct inode *inode); + +/* + * Definitions which augment the buffer_head layer + */ + +/* journaling buffer types */ +#define BJ_None 0 /* Not journaled */ +#define BJ_SyncData 1 /* Normal data: flush before commit */ +#define BJ_AsyncData 2 /* writepage data: wait on it before commit */ +#define BJ_Metadata 3 /* Normal journaled metadata */ +#define BJ_Forget 4 /* Buffer superceded by this transaction */ +#define BJ_IO 5 /* Buffer is for temporary IO use */ +#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ +#define BJ_LogCtl 7 /* Buffer contains log descriptors */ +#define BJ_Reserved 8 /* Buffer is reserved for access by journal */ +#define BJ_Types 9 + +extern int jbd_blocks_per_page(struct inode *inode); + +#ifdef __KERNEL__ + +extern spinlock_t jh_splice_lock; +/* + * Once `expr1' has been found true, take jh_splice_lock + * and then reevaluate everything. + */ +#define SPLICE_LOCK(expr1, expr2) \ + ({ \ + int ret = (expr1); \ + if (ret) { \ + spin_lock(&jh_splice_lock); \ + ret = (expr1) && (expr2); \ + spin_unlock(&jh_splice_lock); \ + } \ + ret; \ + }) + +/* + * A number of buffer state predicates. They test for + * buffer_jbd() because they are used in core kernel code. + * + * These will be racy on SMP unless we're *sure* that the + * buffer won't be detached from the journalling system + * in parallel. + */ + +/* Return true if the buffer is on journal list `list' */ +static inline int buffer_jlist_eq(struct buffer_head *bh, int list) +{ + return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list); +} + +/* Return true if this bufer is dirty wrt the journal */ +static inline int buffer_jdirty(struct buffer_head *bh) +{ + return buffer_jbd(bh) && __buffer_state(bh, JBDDirty); +} + +/* Return true if it's a data buffer which journalling is managing */ +static inline int buffer_jbd_data(struct buffer_head *bh) +{ + return SPLICE_LOCK(buffer_jbd(bh), + bh2jh(bh)->b_jlist == BJ_SyncData || + bh2jh(bh)->b_jlist == BJ_AsyncData); +} + +#ifdef CONFIG_SMP +#define assert_spin_locked(lock) J_ASSERT(spin_is_locked(lock)) +#else +#define assert_spin_locked(lock) do {} while(0) +#endif + +#define buffer_trace_init(bh) do {} while (0) +#define print_buffer_fields(bh) do {} while (0) +#define print_buffer_trace(bh) do {} while (0) +#define BUFFER_TRACE(bh, info) do {} while (0) +#define BUFFER_TRACE2(bh, bh2, info) do {} while (0) +#define JBUFFER_TRACE(jh, info) do {} while (0) + +#endif /* __KERNEL__ */ + +#endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */ + +/* + * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD + * go here. + */ + +#if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE)) + +#define J_ASSERT(expr) do {} while (0) +#define J_ASSERT_BH(bh, expr) do {} while (0) +#define buffer_jbd(bh) 0 +#define buffer_jlist_eq(bh, val) 0 +#define journal_buffer_journal_lru(bh) 0 + +#endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */ +#endif /* _LINUX_JBD_H */ diff --git a/src/kernel-list.h b/src/kernel-list.h new file mode 100644 index 0000000..e07d06b --- /dev/null +++ b/src/kernel-list.h @@ -0,0 +1,112 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = { &name, &name } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +#if (!defined(__GNUC__) && !defined(__WATCOMC__)) +#define __inline__ +#endif + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * Insert a new entry after the specified head.. + */ +static __inline__ void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Insert a new entry at the tail + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/* + * Splice in "list" into "head" + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#endif diff --git a/src/lookup_local.c b/src/lookup_local.c new file mode 100644 index 0000000..f77f00d --- /dev/null +++ b/src/lookup_local.c @@ -0,0 +1,732 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +//header util.h + +#include <stdio.h> +#include <string.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <ext2fs/ext2fs.h> +#include <ext2fs/ext2_io.h> +#include "ext2fsP.h" +#include <ctype.h> +#include <time.h> +#include "dir_list.h" +#include "util.h" +#include "inode.h" + +extern ext2_filsys current_fs ; + +static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + + +// local sub function for printing the dir entry +static int list_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry, + struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *private) +{ + struct ext2_inode inode; + ext2_ino_t ino; + struct tm *tm_p; + time_t modtime; + char name[EXT2_NAME_LEN + 1]; + char tmp[EXT2_NAME_LEN + 16]; + char datestr[80]; + char lbr, rbr; + int thislen,retval = 0; + int col = 0; + struct priv_dir_iterate_struct *ls = (struct priv_dir_iterate_struct *) private; + + thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ? + (dirent->name_len & 0xFF) : EXT2_NAME_LEN; + strncpy(name, dirent->name, thislen); + name[thislen] = '\0'; + ino = dirent->inode; + +//FIXME: + // if ((entry == DIRENT_DELETED_FILE) || (ls->options & DELETED_DIR)) { + if (entry == DIRENT_DELETED_FILE) { + lbr = '<'; + rbr = '>'; + } else { + lbr = rbr = ' '; + } + if (ls->options & PARSE_OPT) { + if (ino && intern_read_inode(ino, &inode)) return 0; + fprintf(stdout,"/%u/%06o/%d/%d/%s/",ino,inode.i_mode,inode.i_uid, inode.i_gid,name); + if (LINUX_S_ISDIR(inode.i_mode)) + fprintf(stdout, "/"); + else + fprintf(stdout, "%lld/", inode.i_size | ((__u64)inode.i_size_high << 32)); + fprintf(stdout, "\n"); + } + else if (ls->options & LONG_OPT) { + if ((ino && (ino <= current_fs->super->s_inodes_count))) { + if (ls->options & ONLY_JOURNAL) + retval = read_journal_inode( ino,&inode, ls->transaction->start); + if (retval || !(ls->options & ONLY_JOURNAL)) + if (intern_read_inode(ino, &inode)) + return 0; + modtime = inode.i_mtime; + tm_p = localtime(&modtime); + sprintf(datestr, "%2d-%s-%4d %02d:%02d", + tm_p->tm_mday, monstr[tm_p->tm_mon], + 1900 + tm_p->tm_year, tm_p->tm_hour, + tm_p->tm_min); + } else { + strcpy(datestr, " "); + memset(&inode, 0, sizeof(struct ext2_inode)); + } + fprintf(stdout, "%c%8u%c %c %4o (%d) %5d %5d ", lbr, ino, rbr, get_inode_mode_type(inode.i_mode),inode.i_mode & 07777 , dirent->name_len >> 8, inode_uid(inode), inode_gid(inode)); + if (LINUX_S_ISDIR(inode.i_mode)) + fprintf(stdout, "%5d", inode.i_size); + else + fprintf(stdout, "%5llu", inode.i_size | + ((unsigned long long) inode.i_size_high << 32)); + fprintf (stdout, " %s %s\n", datestr, name); + } else { + sprintf(tmp, "%c%u%c (%d) %s ", lbr, dirent->inode, rbr, + dirent->rec_len, name); + thislen = strlen(tmp); + + if (col + thislen > 80) { + fprintf(stdout, "\n"); + col = 0; + } + fprintf(stdout, "%s", tmp); + col += thislen; + } + return 0; +} + + +// list dir by using real fs inode and real fs dir blocks an real dir-entry-inodes +void list_dir(ext2_ino_t inode) +{ + int retval; + int flags; + struct priv_dir_iterate_struct ls; + + ls.options = 0; + if (!inode) return; + + ls.options |= LONG_OPT; + ls.options |= DELETED_OPT; + + flags = DIRENT_FLAG_INCLUDE_EMPTY; + if (ls.options & DELETED_OPT) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + + retval = ext2fs_dir_iterate2(current_fs, inode, flags,0, list_dir_proc, &ls); + fprintf(stdout, "\n"); + if (retval) + fprintf(stderr,"Error %d \n", retval); + + return; +} + + + + + +//--------------------------------------------------------------- +/* + * This function checks to see whether or not a potential deleted + * directory entry looks valid. What we do is check the deleted entry + * and each successive entry to make sure that they all look valid and + * that the last deleted entry ends at the beginning of the next + * undeleted entry. Returns 1 if the deleted entry looks valid, zero + * if not valid. + * + * copy of a not public function from e2fsprogs ext2fs_validate_entry() + * use only from local_process_dir_block() + */ +static int local_validate_entry(ext2_filsys fs, char *buf, + unsigned int offset, + unsigned int final_offset) +{ + struct ext2_dir_entry *dirent; + unsigned int rec_len = 0; + +#define DIRENT_MIN_LENGTH 12 + + while ((offset < final_offset) && + (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { + dirent = (struct ext2_dir_entry *)(buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return 0; + + offset += rec_len; + if ((rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) + return 0; + } + + return (offset == final_offset); +} + + + +// convert dir_block for bigendian inclusive deleted entry +static int convert_dir_block(char *buf, int flags){ + errcode_t retval; + char *p, *end; + struct ext2_dir_entry *dirent; + unsigned int name_len, rec_len; + + p = (char *) buf; + end = (char *) buf + current_fs->blocksize; + while (p < end-8) { + dirent = (struct ext2_dir_entry *) p; +#ifdef WORDS_BIGENDIAN + dirent->inode = ext2fs_swab32(dirent->inode); + dirent->rec_len = ext2fs_swab16(dirent->rec_len); + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + name_len = dirent->name_len; +#ifdef WORDS_BIGENDIAN + if (flags & EXT2_DIRBLOCK_V2_STRUCT) + dirent->name_len = ext2fs_swab16(dirent->name_len); +#endif + if ((retval = ext2fs_get_rec_len(current_fs, dirent, &rec_len)) != 0) + return retval; + if ((rec_len < 8) || (rec_len % 4)) { + rec_len = 8; + retval = EXT2_ET_DIR_CORRUPTED; + } else if (((name_len & 0xFF) + 8) > rec_len) + retval = EXT2_ET_DIR_CORRUPTED; + // p += rec_len; + p += ((name_len & 0xFF) + 11) & ~3; + } + return retval; +} + + +/* + * Helper function which is private to this module. Used by + * local_dir_iterate3() (modi of ext2fs_process_dir_block + */ + static int local_process_dir_block(ext2_filsys fs, + blk_t *blocknr, + e2_blkcnt_t blockcnt, + blk_t ref_block EXT2FS_ATTR((unused)), + int ref_offset EXT2FS_ATTR((unused)), + void *priv_data) +{ + struct dir_context *ctx = (struct dir_context *) priv_data; + unsigned int offset = 0; + unsigned int next_real_entry = 0; + int ret = 0; + int changed = 0; + int do_abort = 0; + unsigned int rec_len, size; + int entry; + struct ext2_dir_entry *dirent; + trans_range_t *transaction = NULL; + + if (blockcnt < 0) + return 0; + entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; + + transaction = (trans_range_t*) ((struct priv_dir_iterate_struct*)ctx->priv_data)->transaction; + if (get_last_block(ctx->buf, blocknr, transaction->start, transaction->end)){ + if (ctx->flags & ONLY_JOURNAL ) return BLOCK_ABORT; +//FIXME: if not found dir-block in journal, read from real filesystem and hope is ok ?????? + ctx->errcode = io_channel_read_blk(fs->io, *blocknr, 1, ctx->buf); + } +#ifdef WORDS_BIGENDIAN + if(!ctx->errcode) + ctx->errcode = convert_dir_block(ctx->buf,0); +#endif + if (ctx->errcode) + return BLOCK_ABORT; + + while (offset < fs->blocksize) { + dirent = (struct ext2_dir_entry *) (ctx->buf + offset); + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + if (((offset + rec_len) > fs->blocksize) || + (rec_len < 8) || + ((rec_len % 4) != 0) || + ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { + + ctx->errcode = EXT2_ET_DIR_CORRUPTED; + return BLOCK_ABORT; + } + if (!dirent->inode && + !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) + goto next; + + ret = (ctx->func)(ctx->dir, + (next_real_entry > offset) ? + DIRENT_DELETED_FILE : entry, + dirent, offset, + fs->blocksize, ctx->buf, + ctx->priv_data); + if (entry < DIRENT_OTHER_FILE) + entry++; + + if (ret & DIRENT_CHANGED) { + if (ext2fs_get_rec_len(fs, dirent, &rec_len)) + return BLOCK_ABORT; + changed++; + } + if (ret & DIRENT_ABORT) { + do_abort++; + break; + } +next: + if (next_real_entry == offset) + next_real_entry += rec_len; + + if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { + size = ((dirent->name_len & 0xFF) + 11) & ~3; + + if (rec_len != size) { + unsigned int final_offset = 0; + + final_offset = offset + rec_len; + offset += size; + while (offset < final_offset && + !local_validate_entry(fs, ctx->buf, + offset, + final_offset)) + offset += 4; + continue; + } + } + offset += rec_len; + } + + if (changed) { + ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); + if (ctx->errcode) + return BLOCK_ABORT; + } + if (do_abort) + return BLOCK_ABORT; + return 0; +} + + + + +//------------------------------------------------------ +// Sub function for search the path, use from get_dir3() +static int find_dir_proc(ext2_ino_t dir EXT2FS_ATTR((unused)), + int entry, + struct ext2_dir_entry *dirent, + int offset EXT2FS_ATTR((unused)), + int blocksize EXT2FS_ATTR((unused)), + char *buf EXT2FS_ATTR((unused)), + void *private) +{ + ext2_ino_t ino; + char name[EXT2_NAME_LEN + 1]; + int thislen; + struct priv_dir_iterate_struct *fl = (struct priv_dir_iterate_struct *) private; + + thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ? + (dirent->name_len & 0xFF) : EXT2_NAME_LEN; + if (thislen){ + strncpy(name, dirent->name, thislen); + name[thislen] = '\0'; + ino = dirent->inode; + + if ((ino) && (ino <= current_fs->super->s_inodes_count)) + add_list_item(fl->dir_list,ino,name,(char) entry); + } + return 0; +} + + + + +//------------------------------------------------------- +// local directory iterate for use inode +static errcode_t local_dir_iterate3(ext2_filsys fs, + ext2_ino_t dir, + struct ext2_inode *inode, + int flags, + char *block_buf, + int (*func)(ext2_ino_t dir, + int entry, + struct ext2_dir_entry *dirent, + int offset, + int blocksize, + char *buf, + void *priv_data), + void *priv_data) +{ + struct dir_context ctx; + errcode_t retval; + + EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); + + ctx.dir = dir; + ctx.flags = flags; + if (block_buf) + ctx.buf = block_buf; + else { + retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); + if (retval) + return retval; + } + ctx.func = func; + ctx.priv_data = priv_data; + ctx.errcode = 0; + retval = local_block_iterate3(fs, *inode, BLOCK_FLAG_READ_ONLY, 0, + local_process_dir_block, &ctx); + if (!block_buf) + ext2fs_free_mem(&ctx.buf); + if (retval) + return retval; + return ctx.errcode; +} + + + + + +// list dir over journal inode use real fs dir blocks an real dir-entry-inodes +void list_dir2(ext2_ino_t ino, struct ext2_inode *inode) +{ + int retval; + int flags; + struct priv_dir_iterate_struct ls; + + ls.options = 0; + if (!ino || (! LINUX_S_ISDIR(inode->i_mode))) return; + + ls.options |= LONG_OPT; + ls.options |= DELETED_OPT; + if (! ext2fs_test_inode_bitmap ( current_fs->inode_map, ino )) ls.options |= DELETED_DIR ; + + flags = DIRENT_FLAG_INCLUDE_EMPTY; + if (ls.options & DELETED_OPT) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + + retval = local_dir_iterate3(current_fs,ino, inode, flags,0, list_dir_proc, &ls); + fprintf(stdout, "\n"); + if (retval) + fprintf(stderr,"Error %d \n", retval); + + return; +} + + + + +//-------------------------------------------------- +// +// local sub function +// load dir-index in a dir_list + struct dir_list_head_t* get_dir3(struct ext2_inode* d_inode,ext2_ino_t path_ino, ext2_ino_t ino, + char* path, char* name, + __u32 t_after , __u32 t_before, + int options ) +{ + int retval; + int flags; + struct ext2_inode* inode; + struct ring_buf *i_list = NULL; + r_item *item = NULL; + struct priv_dir_iterate_struct fl; + struct dir_list_head_t * l_dir = NULL; + + if(d_inode) d_inode->i_links_count = 0; //set flag for test + i_list = (struct ring_buf*) get_j_inode_list(current_fs->super, ino); + if (! i_list) return NULL; + + item = get_undel_inode(i_list , t_after , t_before); + if ( item && item->inode ) { + inode = (struct ext2_inode*)item->inode; + if(d_inode) + memcpy(d_inode,inode,current_fs->super->s_inode_size); + if (!ino || (! LINUX_S_ISDIR(inode->i_mode))) + goto errout; + +//get dir + l_dir = new_dir_list ( path_ino,ino, path, name); + if (l_dir) + { + fl.options = options; + fl.dir_list = l_dir; + fl.transaction = &item->transaction; + +// flags = DIRENT_FLAG_INCLUDE_EMPTY; + flags = 0; + if (options & DELETED_OPT ) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + retval = local_dir_iterate3(current_fs,ino, inode, flags,0, find_dir_proc, &fl); + if (retval ) + fprintf(stderr,"Warning: ERROR %d can not found the file: %s\n", retval, path); + } + } + +errout: + if (i_list) + ring_del(i_list); + return (l_dir) ? clean_up_dir_list(l_dir) : NULL ; +} + + + +// main worker function for recover and list +// lookup_local() : search recursiv through the filesystem +// use inode and dirblocks from journal +// flag = 0 only undelete, flag = 2 process all +void lookup_local(char* des_dir, struct dir_list_head_t * dir, __u32 t_after , __u32 t_before, int flag){ + struct dir_list_head_t *d_list = NULL; + struct dir_list_t *lp; + struct ext2_inode* inode = NULL; + struct ring_buf *i_list = NULL; + r_item *item = NULL; + char c = ' '; + int allocated; + + if (! dir) { + d_list = get_dir3(NULL, EXT2_ROOT_INO , EXT2_ROOT_INO , "","", t_after,t_before, flag); + if (!d_list) return; +//recursion + lookup_local(des_dir,d_list, t_after, t_before, flag); + } + else { + if (flag & DOUPLE_QUOTES_LIST) + c = '"'; + lp = GET_FIRST(dir); + inode = malloc(current_fs->super->s_inode_size); + while (lp){ + if (! strcmp(lp->filename,"..")) { + if ((dir->path_inode != lp->inode_nr) && (dir->path_inode != 0)) break; + lp = GET_NEXT(dir,lp); + continue; + } + if (! strcmp(lp->filename,".")) + { +// prefunction for directory + switch (flag & REC_FILTER){ + case LIST_ALL : + if (dir->dir_inode != lp->inode_nr) break; + printf("DIR %lu %c%s%c\n",lp->inode_nr,c,dir->pathname,c); + break; + case LIST_STATUS : + break; + case RECOV_DEL : + break; + case RECOV_ALL : + //create_all_dir(des_dir,dir->pathname, lp->inode_nr); + break; + case HIST_DIR : + add_coll_list(lp->inode_nr); + break; + } + + } + else{ + d_list = get_dir3(inode, dir->dir_inode,lp->inode_nr, + dir->pathname,lp->filename,t_after,t_before, flag); + + if (d_list){ +//recursion for directory + lookup_local(des_dir, d_list, t_after, t_before, flag); + } + else{ +//function for all files apart from dir + switch (flag & REC_FILTER){ + case LIST_ALL : + printf("--- %lu %c%s%s%s%c\n",lp->inode_nr,c,dir->pathname, + ((strlen(dir->pathname) > 0) && strcmp(dir->pathname,"/")) ? "/" : "", + lp->filename,c); + break; + case LIST_STATUS : + allocated = check_file_recover(inode); + if (allocated) + printf("%5u%% %c%s%s%s%c\n",allocated,c,dir->pathname, + ((strlen(dir->pathname) > 0) && strcmp(dir->pathname,"/")) ? "/" : "", + lp->filename,c); + break; + case RECOV_DEL : + if (inode->i_links_count) + recover_file(des_dir,dir->pathname, lp->filename, inode, lp->inode_nr,1); + break; + case RECOV_ALL : + if (inode->i_links_count) + recover_file(des_dir,dir->pathname, lp->filename, inode, lp->inode_nr,0); + break; + case HIST_DIR : + add_coll_list(lp->inode_nr); + break; + } + + } + if (d_list) clear_dir_list(d_list); + d_list = NULL; + } + lp = GET_NEXT(dir,lp); + } + + //if (inode) + free(inode); + + +// postfunction for directory + switch (flag & REC_FILTER){ + case LIST_ALL : + break; + case LIST_STATUS : + break; + case RECOV_DEL : + break; + case RECOV_ALL : + i_list = get_j_inode_list(current_fs->super, dir->dir_inode); + if (! i_list) break; + item = get_undel_inode(i_list , t_after , t_before); + if (item) + set_dir_attributes(des_dir, dir->pathname,(struct ext2_inode*)item->inode); + if (i_list) ring_del(i_list); + break; + case HIST_DIR : + break; + } + + } +if(d_list) + clear_dir_list(d_list); +} + + + +// list dir over journal inode and journal dir blocks an journal dir-entry-inodes +void list_dir3(ext2_ino_t ino, struct ext2_inode *inode, trans_range_t* transaction) +{ + int retval; + int flags; + struct priv_dir_iterate_struct ls; + + ls.options = 0; + if (!ino || (! LINUX_S_ISDIR(inode->i_mode))) return; + + ls.options |= LONG_OPT; + ls.options |= DELETED_OPT; + ls.options |= ONLY_JOURNAL; + + if (! ext2fs_test_inode_bitmap ( current_fs->inode_map, ino )) ls.options |= DELETED_DIR ; + + ls.transaction = transaction; + + flags = DIRENT_FLAG_INCLUDE_EMPTY + ONLY_JOURNAL; + + if (ls.options & DELETED_OPT) flags |= DIRENT_FLAG_INCLUDE_REMOVED; + + retval = local_dir_iterate3(current_fs,ino, inode, flags,0, list_dir_proc, &ls); + fprintf(stdout, "\n"); + if (retval) + fprintf(stderr,"Error %d \n", retval); + + return; +} + + + +// search the Inode for an pathname (recursiv function) +ext2_ino_t local_namei(struct dir_list_head_t * dir, char *path, __u32 t_after , __u32 t_before, int flag){ + struct dir_list_head_t *d_list = NULL; + struct dir_list_t *lp; + ext2_ino_t ret_inode = 0; + char *p_path = NULL; + char *p1; + char c; + char *p2; + + if (! dir) { + d_list = get_dir3(NULL,EXT2_ROOT_INO , EXT2_ROOT_INO , "","", t_after,t_before, flag); + if (!d_list) { + fprintf(stderr,"no rootdir at filesystem found\n"); + return 0; + } + ret_inode = local_namei(d_list, path, t_after, t_before, flag); + } + else { +// Check end recursion + if (!(strlen(path))) { + ret_inode = dir->dir_inode; + goto out; + } + + p_path = (char*) malloc(strlen(path)+1); + p2 = p_path; + p1 = path + strspn(path,"/"); + c = *p1; + while ( c != '/' ){ + *p2++ = c; + if (c == 0) break; + p1++; + c = *p1; + } + if ( c ) *p2 = 0; + + lp = GET_FIRST(dir); + while (lp){ + if (! strcmp(lp->filename,"..")) { + if (dir->path_inode != lp->inode_nr) { + break; + } + } + if (! strcmp(lp->filename,".")) + { + if (dir->dir_inode != lp->inode_nr) { + break; + } + } + else{ + if (strcmp( p_path , lp->filename)) { + lp = GET_NEXT(dir,lp); + continue; + } + d_list = get_dir3(NULL,dir->dir_inode,lp->inode_nr,dir->pathname,lp->filename,t_after,t_before, flag); + if (d_list){ + ret_inode = local_namei(d_list, p1, t_after, t_before, flag); +// break if found + if (ret_inode) goto out; + } + else{ +// recursions end (file find) + ret_inode = lp->inode_nr; + goto out; + } + } + lp = GET_NEXT(dir,lp); + } + } +out: +if(p_path) + free(p_path); +if(d_list) + clear_dir_list(d_list); + +return ret_inode; +} diff --git a/src/recover.c b/src/recover.c new file mode 100644 index 0000000..d3435a0 --- /dev/null +++ b/src/recover.c @@ -0,0 +1,583 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +//header util.h + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <utime.h> + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#include <ext2fs/ext2fs.h> +#include "util.h" +#include "hard_link_stack.h" +#include "inode.h" + + +//defines for error in recover_file +#define CREATE_ERROR 0x80 +#define SEEK_ERROR 0x40 +#define MOVE_ERROR 0x20 +#define CHOWN_ERROR 0x10 +#define CHMOD_ERROR 0x08 +#define UTIME_ERROR 0x04 + +//in this version unused +#define ATTRI_ERROR 0x02 +#define ACL_ERROR 0x01 + + +extern ext2_filsys current_fs; + + +struct privat { + int fd; + char* buf; + char flag; + int error;}; + +struct alloc_stat{ + __u32 allocated; + __u32 not_allocated;}; + + + + +// recover files from a "double quotes" listfile +void recover_list(char *des_dir, char *input_file,__u32 t_after, __u32 t_before, int flag){ + FILE *f; + char *lineptr = NULL ; + char *filename = NULL ; + char *p1 , *p2; + char c; + size_t maxlen; + int l; + size_t got; + ext2_ino_t inode_nr; + struct ring_buf *i_list; + struct ext2_inode* r_inode; + r_item *item = NULL; + + f = fopen(input_file,"a+"); + if (f) { + rewind(f); + maxlen = 512; + lineptr = malloc(maxlen); + filename = malloc(maxlen); + if ((!lineptr) || (!filename)) + goto errout; + while (! (feof(f))){ + got = getline (&lineptr, &maxlen, f); + if (got != -1){ + p1 = strchr(lineptr,'"'); + p2 = strrchr(lineptr,'"'); + if ((p1) && (p2) && (p1 != p2)){ + p1++; + *p2 = 0; + strcpy(filename,p1); + if (*filename == 0) + continue; + inode_nr = local_namei(NULL,filename,t_after,t_before,DELETED_OPT); + if(! inode_nr){ +// printf("no found %s\n",filename); + continue; + } + i_list = get_j_inode_list(current_fs->super, inode_nr); + item = get_undel_inode(i_list,t_after,t_before); + + if (item) { + r_inode = (struct ext2_inode*)item->inode; + if (! LINUX_S_ISDIR(r_inode->i_mode) ) + recover_file(des_dir,"", filename, r_inode, inode_nr ,flag); + } +// else +// printf("no found %s\n",filename); + if (i_list) ring_del(i_list); + } +// else +// printf("no filename found in : \"%s\"",lineptr); + } + } + + } + +errout: + fclose(f); + if (lineptr) + free(lineptr); + if (filename) + free(filename); +return ; +} + + + +// Subfunction for "local_block_iterate3()" for check if the blocks allocated + static int check_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) +{ +//FIXME: + if (*blocknr > fs->super->s_blocks_count) + return BLOCK_ERROR; + struct alloc_stat *stat = priv; + if ( ext2fs_test_block_bitmap ( fs->block_map, *blocknr )) + (stat->allocated)++ ; + else + (stat->not_allocated)++ ; +return 0; +} + + +//Subfunction for "local_block_iterate3()" read a blocks of a long symlink +static int read_syslink_block ( ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) +{ + char *charbuf =((struct privat*)priv)->buf; + __u32 nbytes; + errcode_t retval; + int blocksize = fs->blocksize; + + int allocated = ext2fs_test_block_bitmap ( fs->block_map, *blocknr ); + if ( allocated ){ +// fprintf(stderr,"Block %10lu is allocated.\n",*blocknr); + return (BLOCK_ABORT | BLOCK_ERROR); + } + retval = io_channel_read_blk ( fs->io, *blocknr, 1, charbuf ); + if (retval) + { return (BLOCK_ERROR);} +return 0; +} + + +//Subfunction for "local_block_iterate3()" for recover the blocks of a real file +static int write_block ( ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, + blk_t /*ref_blk*/x, int /*ref_offset*/y, void *priv ) +{ + int fd = ((struct privat*)priv)->fd; + char *charbuf =((struct privat*)priv)->buf; + __u32 nbytes; + errcode_t retval; + int blocksize = fs->blocksize; + + if (((struct privat*)priv)->flag){ + int allocated = ext2fs_test_block_bitmap ( fs->block_map, *blocknr ); + if ( allocated ){ +// fprintf(stderr,"Block %10lu is allocated.\n",*blocknr); + ((struct privat*)priv)->error = BLOCK_ABORT | BLOCK_ERROR ; + return (BLOCK_ABORT | BLOCK_ERROR); + } + } + + retval = io_channel_read_blk ( fs->io, *blocknr, 1, charbuf ); + if (retval){ + ((struct privat*)priv)->error = BLOCK_ERROR ; + return (BLOCK_ERROR); + } + lseek(fd,(unsigned long long )blocksize * blockcnt, SEEK_SET); + + nbytes = write(fd, charbuf, blocksize); + if ((unsigned) nbytes != blocksize){ + fprintf(stderr, "Error while writing file\n"); + ((struct privat*)priv)->error = BLOCK_ERROR ; + return 8; + } +return retval; +} + + +//local check if the target directory existent, (recursive function) +static int check_dir(char* pathname){ + char *buffer; + struct stat filestat; + char *p1; + int retval = 1; + + if(! strlen(pathname)) + return 1; + buffer = malloc(strlen(pathname) +3); + strcpy(buffer,pathname); + p1 = strrchr(buffer,'/'); + if (p1) + *p1 = 0; + if (stat(buffer, &filestat) || (! S_ISDIR(filestat.st_mode)) ){ //&& (filestat.st_mode & S_IRWXU) == S_IRWXU)) + + if (! check_dir(buffer)) { + retval = mkdir(buffer,S_IRWXU); + } + } + else + retval = 0; + if(buffer) free(buffer); + return retval; +} + + + +//subfunction for recover_file +static char* get_error_string(char* buf, int error){ + int mask = 0x80; + int i; + for (i = 0; i< 8 ; i++){ + *(buf+i) = (error & mask) ? '-' : 'X' ; + mask /= 2 ; + } + *(buf+i) = 0; +return buf; +} + + + +// recover Datafile +int recover_file( char* des_dir,char* pathname, char* filename, struct ext2_inode *inode, ext2_ino_t inode_nr, int flag){ + int rec_error = 255; + char err_string[9]; + int retval =-1; + int hardlink = 0; + char i = 0; + char *buf=NULL; + struct privat priv; + struct stat filestat; + char *helpname = NULL; + char *recovername = NULL; + char *linkname = NULL; + char *p1; + unsigned long long i_size; + struct utimbuf touchtime; + mode_t i_mode; + int major, minor, type; + + p1 = pathname; + while (*p1 == '/') p1++; + helpname = malloc(strlen(des_dir) + 15); + recovername = malloc(strlen(des_dir) + strlen(pathname) + strlen(filename) +10); + if (helpname && recovername){ + strcpy(helpname,des_dir); + strcat(helpname,"/"); + strcpy(recovername,helpname); + strcat(recovername,p1); + if (strlen(p1)) + strcat(recovername,"/"); + strcat(recovername,filename); + while (!(stat(recovername, &filestat)) && (i<5)){ + strcat(recovername,"#"); + i++; + } + + p1 = strchr(helpname,0); + sprintf(p1,"#%u#",inode_nr); + unlink (helpname); + + priv.flag = flag; + +//FIXME: hardlink + if (inode->i_links_count > 1){ + p1 = check_link_stack(inode_nr, inode->i_generation); + if (p1) { + //hardlink found + if (! stat(p1, &filestat)){ + retval = check_dir(recovername); + if (retval) + fprintf(stderr,"Unknown error at target directory by file: %s\ntrying to continue normal\n", recovername); + else { + if (! link(p1,recovername)){ + if (match_link_stack(inode_nr, inode->i_generation)) + rec_error -= CREATE_ERROR ; + goto out; + } + else{ + rec_error -= CREATE_ERROR ; + } + } + } + } + else{ + //flag as hardlink + hardlink = 1; + } + } + + + switch (inode->i_mode & LINUX_S_IFMT){ +//regular File; + case LINUX_S_IFREG : + priv.fd = open(helpname, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRWXU); + if (! priv.fd ){ + fprintf(stderr,"Error: open File %s for writing\n",helpname); + retval = errno; + goto errout; + } + buf=malloc(current_fs->blocksize); + if (buf) { + priv.buf = buf; + priv.error = 0; + // iterate Data Blocks and if not allocated, write to file + retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, write_block, &priv ); + if (retval || priv.error){ + // error or blocks allocated , we delete the tempfile and goto out + close(priv.fd); + unlink(helpname); + retval = -1; + goto errout; + } + else{ + i_size = (unsigned long long)(inode->i_size | ((unsigned long long)inode->i_size_high << 32)); + retval = ftruncate(priv.fd,i_size); + if (retval){ + rec_error -= SEEK_ERROR ; + } + } + + } + else { + fprintf(stderr,"ERROR: can no allocate memory\n"); + retval = -1; + close(priv.fd); + goto errout; + } + close(priv.fd); + break; + +//symbolic link + case LINUX_S_IFLNK : + if(inode->i_blocks){ +//FIXME: not work if linkname bigger as the blocksize + buf = malloc(current_fs->blocksize); + if (buf) { + priv.buf = buf; + retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, read_syslink_block, &priv ); + } + else { + fprintf(stderr,"ERROR: can no allocate memory\n"); + retval = -1; + goto errout; + } + } + else { + int i; + + if(! inode->i_size || (inode->i_size > 60)) + goto errout; + buf = malloc(inode->i_size + 1); + linkname = (char*) &(inode->i_block[0]); + for (i = 0; i < inode->i_size ; i++){ + *(buf+i) = (char) *linkname; + linkname++; + } + *(buf+i) = 0; + } + linkname = buf; + retval = symlink(linkname, helpname); + if (retval){ + rec_error -= (CREATE_ERROR + SEEK_ERROR); + } + break; + +//block or char device + case LINUX_S_IFBLK : + case LINUX_S_IFCHR : + type = (LINUX_S_ISBLK(inode->i_mode)) ? S_IFBLK : S_IFCHR ; + + if (inode->i_block[0]) { + major = (inode->i_block[0] >> 8) & 255; + minor = inode->i_block[0] & 255; + } else { + major = (inode->i_block[1] & 0xfff00) >> 8; + minor = ((inode->i_block[1] & 0xff) | + ((inode->i_block[1] >> 12) & 0xfff00)); + } + retval = mknod(helpname, type ,makedev(major, minor)); + if (retval){ + rec_error -= (CREATE_ERROR + SEEK_ERROR); + } + break; + +//fifo + case LINUX_S_IFIFO: + retval = mkfifo(helpname, S_IRWXU); + if (retval){ + rec_error -= (CREATE_ERROR + SEEK_ERROR); + } + break; + +//socket and not define + default: + retval = -1; + goto errout; + break; + } //end switch + + + retval = check_dir(recovername); + if (retval) + fprintf(stderr,"Unknown error at target directory by file: %s \ntrying to continue normal\n", recovername); + else { + retval = rename(helpname,recovername); + if (retval && (errno != EEXIST)){ + rec_error -= MOVE_ERROR ; + } + retval = chown(recovername,inode_uid(*inode), inode_gid(*inode)); + if (retval){ + rec_error -= CHOWN_ERROR ; + } + + if (! LINUX_S_ISLNK(inode->i_mode)){ + retval = chown(recovername,inode_uid(*inode), inode_gid(*inode)); + if (retval){ + rec_error -= CHOWN_ERROR ; + } + i_mode = inode->i_mode & 07777; + retval = chmod(recovername,i_mode); + if (retval){ + rec_error -= CHMOD_ERROR ; + } + touchtime.actime = inode->i_atime; + touchtime.modtime = inode->i_mtime; + retval = utime(recovername,&touchtime); + if (retval){ + rec_error -= UTIME_ERROR ; + } + } + else { + retval = lchown (recovername, inode_uid(*inode), inode_gid(*inode)); + if (retval){ + rec_error -= CHOWN_ERROR ; + } + } + } +if ((hardlink) && (rec_error & SEEK_ERROR )) + add_link_stack(inode_nr, inode->i_links_count, recovername,inode->i_generation ); + +out: + printf("%s %s\n",get_error_string(err_string,rec_error),recovername); + + +errout: + if(buf) free(buf); + if (helpname) free(helpname); + if (recovername) free(recovername); + + } //helpname +return retval; +} + + + + +// check Datafile return the percentage of not allocated blocks +int check_file_recover(struct ext2_inode *inode){ + int retval =-1; + struct alloc_stat stat; + + stat.allocated = 0; + stat.not_allocated = 0; + if (! inode->i_blocks) + retval = 100; + else{ + retval = local_block_iterate3 ( current_fs, *inode, BLOCK_FLAG_DATA_ONLY, NULL, check_block, &stat ); + if ( retval ) return 0; + + if (stat.not_allocated) + retval = ((stat.not_allocated * 100) / (stat.allocated + stat.not_allocated)); + } +return retval; +} + + + +/* +//FIXME: If we ever need this function, we rewrite this +int create_all_dir(char* des_dir,char* pathname, ext2_ino_t ino_nr ){ + char *fullname; + char *p1; + int retval; + + if (ino_nr == EXT2_ROOT_INO) + return 0; + + fullname = malloc(strlen(des_dir) + strlen(pathname) + 3); + if (fullname){ + p1 = pathname; + while (*p1 == '/') p1++; + strcpy(fullname,des_dir); + strcat(fullname,"/"); + strcat(fullname,p1); + retval = mkdir(fullname,S_IRWXU); + if (retval && (errno != EEXIST)) + fprintf(stderr,"Error: mkdir %s\n", fullname); + free(fullname); + } +return retval; +} +*/ + + +//set all attributes for directory +void set_dir_attributes(char* des_dir,char* pathname,struct ext2_inode *inode){ + char *fullname; + char *p1; + char err_string[9]; + int retval, rec_error = 255; + mode_t i_mode; + struct stat filestat; + struct utimbuf touchtime; + + fullname = malloc(strlen(des_dir) + strlen(pathname) + 3); + if (fullname){ + p1 = pathname; + while (*p1 == '/') p1++; + strcpy(fullname,des_dir); + strcat(fullname,"/"); + strcat(fullname,p1); + + retval = check_dir(fullname); + if ( stat(fullname, &filestat)){ + retval = mkdir(fullname,S_IRWXU); + if (retval) + rec_error -= (CREATE_ERROR + SEEK_ERROR); + } + + retval = chown(fullname,inode_uid(*inode), inode_gid(*inode)); + if (retval){ + rec_error -= CHOWN_ERROR ; + } + + i_mode = inode->i_mode & 07777; + retval = chmod(fullname,i_mode); + if (retval){ + rec_error -= CHMOD_ERROR ; + } + + touchtime.actime = inode->i_atime; + touchtime.modtime = inode->i_mtime; + retval = utime(fullname,&touchtime); + if (retval){ + rec_error -= UTIME_ERROR ; + } + printf("%s %s\n",get_error_string(err_string,rec_error),fullname); + + free(fullname); + } +} diff --git a/src/ring_buf.c b/src/ring_buf.c new file mode 100644 index 0000000..016b643 --- /dev/null +++ b/src/ring_buf.c @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +// A special doubly linked ringbuffer for manage journalinode + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include "ring_buf.h" + + +r_item* r_item_new(int i_size){ + r_item* this = NULL; + + this = (r_item*) malloc(sizeof(r_item)) ; + if (! this) return NULL; + this->prev = this; + this->next = this; + this->transaction.start = 0; + this->transaction.end = 0; + this->inode = (struct ext2_inode_large*) malloc(i_size); + if (! this->inode ) { + free(this); + return NULL; + } +return this; +}; + +struct ring_buf* ring_new(int i_size, __u32 nr){ + struct ring_buf *head; + head = (struct ring_buf*) malloc(sizeof(struct ring_buf)); + if (! head ) return NULL; + head->count = 0; + head->del_flag = 0; + head->reuse_flag = 0; + head->i_size = i_size; + head->nr = nr; +return head; +}; + +r_item* r_item_add(struct ring_buf *head){ + r_item* item = r_item_new(head->i_size); + if (item) { + if (! head->count) { head->begin = item;} + else { + item->next = head->begin; + item->prev = head->begin->prev; + head->begin->prev->next = item; + head->begin->prev = item; + } + head->count++; +// item->transaction = *trans; + } + return item; +}; + +void ring_del(struct ring_buf *head){ + r_item* item; + r_item* old; + old = NULL; + if (head->count){ + item = head->begin->next; + head->begin->next = NULL; + while (item){ + if (item->inode) free(item->inode); + old = item; + item = item->next; + free(old); + } + } + free(head); + head = NULL; +}; + +r_item* r_begin(struct ring_buf *head){ + if (head->count > 1) + while (head->begin->inode->i_ctime > head->begin->prev->inode->i_ctime) + head->begin = head->begin->prev ; + return head->begin; +} + + + diff --git a/src/ring_buf.h b/src/ring_buf.h new file mode 100644 index 0000000..c881ebb --- /dev/null +++ b/src/ring_buf.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +// A special doubly linked ringbuffer for manage journalinode + +#ifndef RING_BUF_H +#define RING_BUF_H + +/* ext3/4 libraries */ +#include <ext2fs/ext2fs.h> + + +typedef struct transaction_range{ +__u32 start; +__u32 end; +} trans_range_t; + + +struct ring_buf_item { + struct ring_buf_item *prev; + struct ring_buf_item *next; + struct ext2_inode_large *inode; + trans_range_t transaction; +}; + +struct ring_buf { + struct ring_buf_item * begin; + int count; + int i_size; + __u32 nr; + char del_flag; + char reuse_flag; +}; + +typedef struct ring_buf_item r_item; + + +static inline r_item* r_first(struct ring_buf *head){ + return (head->count) ? head->begin : NULL ;} + +static inline r_item* r_last(struct ring_buf *head){ + return (head->count) ? head->begin->prev : NULL ; } + +static inline r_item* r_next(r_item *item){ + return item->next;} + +static inline r_item* r_prev(r_item *item){ + return item->prev;} + +static inline int r_get_count(struct ring_buf *head){ + return head->count;} + + +r_item* r_item_new( int ); +struct ring_buf* ring_new( int , __u32); +r_item* r_item_add(struct ring_buf*); +void ring_del(struct ring_buf*); +r_item* r_begin(struct ring_buf*); +#endif diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..e7a785b --- /dev/null +++ b/src/util.c @@ -0,0 +1,442 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <time.h> + +/* ext3/4 libraries */ +#include <ext2fs/ext2fs.h> +#include <ext2fs/ext2_io.h> +#include <e2p/e2p.h> + +#include "util.h" +#include "ext2fsP.h" + +// default maxcount for histrogram +#define HIST_COUNT 10 + + +extern ext2_filsys current_fs; +static struct inode_nr_collect *collect ; + +// privat struct for histogram +struct time_counter{ + __u32 time; + __u32 c_count; + __u32 d_count; + __u32 cr_count; +}; + + +//Sub function for print header for histogram +static void dump_hist_header(char* type, __u32 after){ + fprintf(stdout,"|-----------%s----------------- after -------------------- %s",type,time_to_string(after)); + return; +} + + +//Sub function for print the histogram +static void dump_hist(struct time_counter* p_hist, int cm, __u32 after, __u32 befor , __u16 crt_found ){ + int i,j ; + float x, step = 10.1 ; + +// FIXME: this function can optimize + +// c_time +dump_hist_header("c_time Histogram",after); +for (i=1;i <= cm ;i++) if (step < (p_hist+i)->c_count) step = (p_hist+i)->c_count; +step = step / 50; +for (i=1;i <= cm ;i++){ + fprintf(stdout,"%10lu : %8lu |", (p_hist+i)->time , (p_hist+i)->c_count); + for (j = 0,x = (p_hist+i)->c_count/50 ; j < 50 ; j++, x+=step ) + fprintf(stdout,"%c",(x<(p_hist+i)->c_count) ? '*' : ' '); + fprintf(stdout,"| %s", time_to_string((__u32)(p_hist+i)->time)); +} +// d_time +fprintf(stdout,"\n\n"); +dump_hist_header("d_time Histogram",after); +step = 10.1 ; +for (i=1;i <= cm ;i++) if (step < (p_hist+i)->d_count) step = (p_hist+i)->d_count; +step = step / 50; +for (i=1;i <= cm ;i++){ + fprintf(stdout,"%10lu : %8lu |", (p_hist+i)->time , (p_hist+i)->d_count); + for (j = 0,x = (p_hist+i)->d_count/50 ; j < 50 ; j++, x+=step ) + fprintf(stdout,"%c",(x<(p_hist+i)->d_count) ? '*' : ' '); + fprintf(stdout,"| %s", time_to_string((__u32)(p_hist+i)->time)); +} +// cr_time +if (crt_found){ + fprintf(stdout,"\n\n"); + dump_hist_header("cr_time Histogram",after); + step = 10.1 ; + for (i=1;i <= cm ;i++) if (step < (p_hist+i)->cr_count) step = (p_hist+i)->cr_count; + step = step / 50; + for (i=1;i <= cm ;i++){ + fprintf(stdout,"%10lu : %8lu |", (p_hist+i)->time , (p_hist+i)->cr_count); + for (j = 0,x = (p_hist+i)->cr_count/50 ; j < 50 ; j++, x+=step ) + fprintf(stdout,"%c",(x<(p_hist+i)->cr_count) ? '*' : ' '); + fprintf(stdout,"| %s", time_to_string((__u32)(p_hist+i)->time)); + } +} +return; +} + + +// analyse and print time stamp of all fs inode (histogram function) +void read_all_inode_time(ext2_filsys fs, __u32 t_after, __u32 t_before, int flag) +{ +struct ext2_group_desc *gdp; +char *buf= NULL; +int x, j, cm, zero_flag, retval; +__u32 blocksize , inodesize , inode_max , inode_per_group, block_count; +__u16 inode_per_block , inode_block_group, group; +blk_t block_nr; +__u32 i, c_time, d_time, cr_time; +struct ext2_inode_large *inode; +__u16 crt_found = 0; + +cm = (flag) ? 2*HIST_COUNT : HIST_COUNT ; + +struct time_counter hist[(HIST_COUNT * 2)+1]; +for (i = 0 ; i<= cm; i++){ + hist[i].c_count = 0; + hist[i].d_count = 0; + hist[i].cr_count = 0; + hist[i].time = ((t_before - t_after)/ cm * i) + t_after; +} + +blocksize = fs->blocksize; +inodesize = fs->super->s_inode_size; +inode_max = fs->super->s_inodes_count; +inode_per_group = fs->super->s_inodes_per_group; +buf = malloc(blocksize); + +inode_per_block = blocksize / inodesize; +inode_block_group = inode_per_group / inode_per_block; + +for (group = 0 ; group < fs->group_desc_count ; group++){ + gdp = &fs->group_desc[group]; + zero_flag = 0; + + // NEXT GROUP IF INODE NOT INIT + if (gdp->bg_flags & (EXT2_BG_INODE_UNINIT)) continue; + + // SET ZERO-FLAG IF FREE INODES == INODE/GROUP for fast ext3 + if (gdp->bg_free_inodes_count == inode_per_group) zero_flag = 1; + +//FIXME for struct ext4_group_desc 48/64BIT + for (block_nr = gdp->bg_inode_table , block_count = 0 ; + block_nr < (gdp->bg_inode_table + inode_block_group); block_nr++, block_count++) { + + // break if the first block only zero inode + if ((block_count ==1) && (zero_flag == (inode_per_block + 1))) break; + + retval = read_block ( fs , &block_nr , buf); + if (retval) return; + + for (i = (group * inode_per_group)+(block_count * inode_per_block) + 1 ,x = 0; + x < inode_per_block ; i++ , x++){ + + if (i >= inode_max) break; + inode = (struct ext2_inode_large*) (buf + (x*inodesize)); + c_time = ext2fs_le32_to_cpu(inode->i_ctime); + if (! c_time) { + if(zero_flag) zero_flag++ ; + continue; + } + + d_time = ext2fs_le32_to_cpu(inode->i_dtime); +//FIXME for bigendian + cr_time = ((inodesize > EXT2_GOOD_OLD_INODE_SIZE) && (ext2fs_le16_to_cpu(inode->i_extra_isize) >= 24)) ? + ext2fs_le32_to_cpu(inode->i_crtime) : 0 ; + + for (j=1;j <= cm ; j++){ + if ((d_time < hist[j].time) && (d_time > hist[j-1].time)){ + hist[j].d_count++; + break; + } + + if (cr_time){ + if ((cr_time < hist[j].time) && (cr_time > hist[j-1].time)){ + hist[j].cr_count++; + crt_found = 1; + cr_time = 0 ; + } + } + + if ((c_time < hist[j].time) && (c_time > hist[j-1].time)){ + hist[j].c_count++; + break; + } + } + } + } +} + + dump_hist(hist, cm, t_after, t_before, crt_found); + + if(buf) { + free(buf); + buf = NULL; + } +return; +} + + + +// print the history of collectlist +void print_coll_list(__u32 t_after, __u32 t_before, int flag){ + + int j, cm; + + __u32 c_time, d_time, cr_time; + ext2_ino_t i; + ext2_ino_t *pointer; + char inode_buf[256]; + struct ext2_inode_large *inode = (struct ext2_inode_large *)inode_buf; + __u16 crt_found = 0; + int inode_size = EXT2_INODE_SIZE(current_fs->super); + + + cm = (flag) ? 2*HIST_COUNT : HIST_COUNT ; + + struct time_counter hist[(HIST_COUNT * 2)+1]; + for (i = 0 ; i<= cm; i++){ + hist[i].c_count = 0; + hist[i].d_count = 0; + hist[i].cr_count = 0; + hist[i].time = ((t_before - t_after)/ cm * i) + t_after; + } + pointer = collect->list; + for (i = 0; i < collect->count; i++ ,pointer++){ + intern_read_inode_full(*pointer, inode_buf , (inode_size > 256) ? 256 : inode_size ); + c_time = inode->i_ctime; + d_time = inode->i_dtime; + cr_time = ((inode_size > EXT2_GOOD_OLD_INODE_SIZE) && (inode->i_extra_isize >= 24)) ? inode->i_crtime : 0 ; + + for (j=1;j <= cm ; j++){ + if ((d_time < hist[j].time) && (d_time > hist[j-1].time)){ + hist[j].d_count++; + break; + } + if (cr_time){ + if ((cr_time < hist[j].time) && (c_time > hist[j-1].time)){ + hist[j].cr_count++; + crt_found = 1; + cr_time = 0 ; + } + } + if ((c_time < hist[j].time) && (c_time > hist[j-1].time)){ + hist[j].c_count++; + break; + } + } + } + + dump_hist(hist, cm, t_after, t_before, crt_found); + if (collect->list) free(collect->list); + if (collect){ + free(collect); + collect = NULL; + } +return; +} + + +// add inodenumber in a collectlist + void add_coll_list(ext2_ino_t ino_nr ){ + ext2_ino_t *pointer; + ext2_ino_t i; + + if (!collect){ + collect = malloc(sizeof(struct inode_nr_collect)); + collect->list = malloc(ALLOC_SIZE * sizeof(ext2_ino_t)); + if (!collect->list){ + free(collect); + fprintf(stderr,"ERROR : can not allocate memory\n"); + return ; + } + collect->count = 0; + } + pointer = collect->list; + for ( i = 0 ; i < collect->count; i++, pointer++ ){ + if (*pointer == ino_nr) + return ; + } + if ( i && (! (i % ALLOC_SIZE ))) { +// we must allocate more memory now + pointer = malloc((collect->count + ALLOC_SIZE +1) *sizeof(ext2_ino_t)); + if (!pointer){ + fprintf(stderr,"ERROR : can not allocate memory\n"); + return; + } + memcpy(pointer,collect->list,(collect->count *sizeof(ext2_ino_t))); + free(collect->list); + collect->list = pointer; + } + + *((collect->list) + i) = ino_nr; + collect->count++ ; +return; +} + + + +//hexdump of block or data +void blockhex (FILE *out_file, void *buf, int flag, int blocksize) +{ + int i,j; + int *intp; + char *charp_0 , *charp_1 ; + unsigned char c; + + intp = (int *) buf; + charp_0 = (unsigned char *) buf; + charp_1 = (char *) buf; + + for (i=0; i<blocksize; i+=16) { + fprintf(out_file, " %04x: ", i); + if (flag) for (j=0; j<16; j+=4) fprintf(out_file, "%08x ", *intp++); + else + for (j=0; j<16; j++) fprintf(out_file, "%02x ", 0xff & *charp_0++); + fprintf(out_file, " "); + for (j=0; j<16; j++) { + c = *charp_1++; + if (c < ' ' || c >= 127) + c = '.'; + fprintf(out_file, "%c", c); + } + fprintf(out_file, "\n"); + } +} + + +//get inode type +char get_inode_mode_type( __u16 i_mode) +{ + char type; + if (LINUX_S_ISDIR(i_mode)) type = 'd'; + else if (LINUX_S_ISREG(i_mode)) type = '_'; + else if (LINUX_S_ISLNK(i_mode)) type = 'l'; + else if (LINUX_S_ISBLK(i_mode)) type = 'b'; + else if (LINUX_S_ISCHR(i_mode)) type = 'c'; + else if (LINUX_S_ISFIFO(i_mode)) type = 'f'; + else if (LINUX_S_ISSOCK(i_mode)) type = 's'; + else type = ' '; + return type; +} + + + + + +/* + * This function takes a __u32 time value and converts it to a string, + * using ctime + */ +char *time_to_string(__u32 cl) +{ + static int do_gmt = -1; + time_t t = (time_t) cl; + const char *tz; + + if (do_gmt == -1) { + /* The diet libc doesn't respect the TZ environemnt variable */ + tz = getenv("TZ"); + if (!tz) + tz = ""; + do_gmt = !strcmp(tz, "GMT"); + } + + return asctime((do_gmt) ? gmtime(&t) : localtime(&t)); +} + + + +/* + * This routine returns 1 if the filesystem is not open, and prints an + * error message to that effect. + */ +int check_fs_open(char *name) +{ + if (!current_fs) { + fprintf(stderr, "Filesystem not open\n"); + return 1; + } + return 0; +} + + +/* + * This function resets the libc getopt() function, which keeps + * internal state. Bad design! Stupid libc API designers! No + * biscuit! + * + * BSD-derived getopt() functions require that optind be reset to 1 in + * order to reset getopt() state. This used to be generally accepted + * way of resetting getopt(). However, glibc's getopt() + * has additional getopt() state beyond optind, and requires that + * optind be set zero to reset its state. So the unfortunate state of + * affairs is that BSD-derived versions of getopt() misbehave if + * optind is set to 0 in order to reset getopt(), and glibc's getopt() + * will core dump if optind is set 1 in order to reset getopt(). + * + * More modern versions of BSD require that optreset be set to 1 in + * order to reset getopt(). Sigh. Standards, anyone? + * + * We hide the hair here. + */ +void reset_getopt(void) +{ +#if defined(__GLIBC__) || defined(__linux__) + optind = 0; +#else + optind = 1; +#endif +#ifdef HAVE_OPTRESET + optreset = 1; /* Makes BSD getopt happy */ +#endif +} + + +//function for check argument by optarg +unsigned long parse_ulong(const char *str, const char *cmd, + const char *descr, int *err) +{ + char *tmp; + unsigned long ret; + + ret = strtoul(str, &tmp, 0); + if (*tmp == 0) { + if (err) + *err = 0; + return ret; + } + fprintf(stderr, "Bad %s - %s\n", descr, str); + if (err) + *err = 1; + else + exit(1); + return 0; +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..cf706aa --- /dev/null +++ b/src/util.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2010 by Roberto Maar * + * robi@users.berlios.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef UTIL_H +#define UTIL_H + + +//status for control flag list-functions +#define LONG_OPT 0x0001 +#define DELETED_OPT 0x0002 +#define PARSE_OPT 0x0004 +#define ONLY_JOURNAL 0x0008 +#define DELETED_DIR 0x0010 + +// control flags for recover- and listmodus +#define DOUPLE_QUOTES_LIST 0x0100 +#define LIST_ALL 0x1000 +#define LIST_STATUS 0x2000 +#define RECOV_DEL 0x4000 +#define RECOV_ALL 0x8000 +#define HIST_DIR 0x0800 +#define REC_FILTER 0xF800 +#define REC_DIR_NEEDED RECOV_ALL | RECOV_DEL + + +/* Definitions to allow ext4magic compilation with old e2fsprogs */ +#ifndef EXT4_EXTENTS_FL +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#endif + + +#include "ring_buf.h" +#include "dir_list.h" + +//struct for iterate directory +struct priv_dir_iterate_struct { + // char *name; + // char *path; + // __u32 inode; + // ext2_ino_t *found_inode; + // int col; + int options; + struct dir_list_head_t *dir_list; +//FIXME; + trans_range_t *transaction; +}; + +//struct for inode position +struct inode_pos_struct { + blk_t block; + int offset; + int size; +}; + +//struct for a simple inodenumberlist +struct inode_nr_collect{ + ext2_ino_t count; + ext2_ino_t *list; +}; +#define ALLOC_SIZE 1024 + +// public functions util.c +void read_all_inode_time(ext2_filsys , __u32 , __u32 , int ); //analyse an print histogram +void print_coll_list(__u32, __u32, int); //print the history of collectlist +void add_coll_list(ext2_ino_t );// add inodenumber in a collectlist +void blockhex (FILE* , void*, int , int); //hexdump +char get_inode_mode_type( __u16); //get filetype of inode +//public helper functions util.c +char *time_to_string(__u32); +int check_fs_open(char*); +void reset_getopt(void); +unsigned long parse_ulong(const char* , const char* , const char* , int* ); + + +// public functions lookup_local.c +void list_dir(ext2_ino_t inode); //list dir (using normal functions from ext2fs) +void list_dir2(ext2_ino_t, struct ext2_inode*); //list dir (search in journal ; not automatical use the real inode from fs) +void list_dir3(ext2_ino_t, struct ext2_inode*, trans_range_t* ); //list (search over journal; both inode as well journaldirblocks) +struct dir_list_head_t* get_dir3(struct ext2_inode*,ext2_ino_t, ext2_ino_t,char*, char*,__u32,__u32,int ); //directory finder +void lookup_local(char*, struct dir_list_head_t*, __u32 , __u32 , int ); // main worker function for recover and list +ext2_ino_t local_namei(struct dir_list_head_t*, char* , __u32, __u32, int);// search the Inode for an pathname (use journal data) + + +//public functions recover.c +void recover_list(char*, char*,__u32, __u32, int); // recover files from a "double quotes" listfile +int recover_file( char* ,char* , char* , struct ext2_inode* , ext2_ino_t, int); //recover all filetypes +int check_file_recover(struct ext2_inode*); // return percentage of not allocated blocks +void set_dir_attributes(char* ,char* ,struct ext2_inode*); //set owner,file mode bits an timestamps for directory + +#endif |