[v5,4/4] ext2fs: automaticlly open backup superblocks

Message ID 20180526224600.46905-5-artem.blagodarenko@gmail.com
State New
Headers show
Series
  • [v5,1/4] ext2fs: opening filesystem code refactoring
Related show

Commit Message

Artem Blagodarenko May 26, 2018, 10:46 p.m.
e2image and e2fsck automatically try to open some backup superblocks,
if only blocksize is set or passed superblock can't be opened.
Try few backup superblocks (e.g. {1, 3, 5, 7, 9} * blocksize * 8).

This code is moved to lib/support/.

Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com>
---
 e2fsck/e2fsck.h         |   2 -
 e2fsck/message.c        |   3 +-
 e2fsck/unix.c           |  16 ++++--
 e2fsck/util.c           |  73 --------------------------
 lib/support/Makefile.in |   8 ++-
 lib/support/sb_backup.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/support/sb_backup.h |  20 +++++++
 misc/e2image.c          |   7 +++
 8 files changed, 184 insertions(+), 82 deletions(-)

Comments

Andreas Dilger June 4, 2018, 5:58 p.m. | #1
On May 26, 2018, at 4:46 PM, Artem Blagodarenko <artem.blagodarenko@gmail.com> wrote:
> 
> e2image and e2fsck automatically try to open some backup superblocks,
> if only blocksize is set or passed superblock can't be opened.
> Try few backup superblocks (e.g. {1, 3, 5, 7, 9} * blocksize * 8).
> 
> This code is moved to lib/support/.

I don't recall seeing an explanation of why this is in lib/support/
instead of lib/ext2fs?  Maybe as ext2fs_open_backups(), or is there
some reason we don't want to export this function?

> Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com>
> ---
> 
> diff --git a/e2fsck/message.c b/e2fsck/message.c
> index 727f71d5..415d7609 100644
> --- a/e2fsck/message.c
> +++ b/e2fsck/message.c
> @@ -465,7 +465,8 @@ static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs,
> 		fprintf(f, "%*lld", width, (long long) ctx->blkcount);
> 		break;
> 	case 'S':
> -		fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL));
> +		fprintf(f, "%llu", get_first_backup_sb(NULL, NULL, fs,
> +							  NULL, NULL));

(style) align after '('

> diff --git a/e2fsck/unix.c b/e2fsck/unix.c
> index 491e9eb6..1a5e556f 100644
> --- a/e2fsck/unix.c
> +++ b/e2fsck/unix.c
> @@ -1479,11 +1479,19 @@ restart:
> 				retval ? _("Superblock invalid,") :
> 				_("Group descriptors look bad..."));
> 			orig_superblock = ctx->superblock;
> -			get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
> -			if (fs)
> -				ext2fs_close_free(&fs);
> 			orig_retval = retval;
> -			retval = try_open_fs(ctx, flags, io_ptr, &fs);
> +			retval = try_backups(ctx->filesystem_name,
> +						    ctx->io_options,
> +						    flags,
> +						    &ctx->superblock,
> +						    &ctx->blocksize, io_ptr,
> +						    &fs);

(style) align after '(', pack onto fewest lines possible

> diff --git a/lib/support/sb_backup.c b/lib/support/sb_backup.c
> new file mode 100644
> index 00000000..5660e237
> --- /dev/null
> +++ b/lib/support/sb_backup.c

(style) remove extra blank lines

> +blk64_t get_first_backup_sb(blk64_t *superblock, unsigned int *block_size,
> +			       ext2_filsys fs, const char *name,
> +			       io_manager manager)

(style) align after '('

> +{
> +	struct ext2_super_block *sb;
> +	io_channel		io = NULL;
> +	void			*buf = NULL;
> +	int			try_blocksize;
> +	blk64_t			try_superblock, ret_sb = 8193;
> +
> +	/* superblock and block_size can be NULL if fs->super is passed */
> +	if (fs && fs->super) {
> +		ret_sb = (fs->super->s_blocks_per_group +
> +			  fs->super->s_first_data_block);

(style) no need for parenthesis here

> +		if (superblock)
> +			*superblock = ret_sb;
> +		if(block_size)

(style) space after "if"

> +}
> +
> +

(style) remove extra blank line

> +errcode_t try_backups(const char *name, const char *io_options,
> +			    int flags, blk64_t *superblock,
> +			    unsigned int *block_size, io_manager manager,
> +			    ext2_filsys *ret_fs)

(style) align after '('

> +{
> +	errcode_t retval;
> +	blk64_t try_block_number;
> +	unsigned int i;
> +
> +	/*
> +	 * Get first superblock location based on heuristic.
> +	 * Blocksize is also returned and used to find next
> +	 * superblock copy location.
> +	 */
> +	try_block_number = get_first_backup_sb(superblock, block_size,
> +						  *ret_fs, name, manager);

(style) align after '('

> +
> +}
> +
> +

(style) remove blank lines at end

> diff --git a/lib/support/sb_backup.h b/lib/support/sb_backup.h
> new file mode 100644
> index 00000000..f1d48bff
> --- /dev/null
> +++ b/lib/support/sb_backup.h
> @@ -0,0 +1,20 @@
> +blk64_t get_first_backup_sb(blk64_t *superblock, unsigned int *block_size,
> +			       ext2_filsys fs, const char *name,
> +			       io_manager manager);
> +
> +errcode_t try_backups(const char *name, const char *io_options,
> +			    int flags, blk64_t *superblock,
> +			    unsigned int *block_size, io_manager manager,
> +			    ext2_filsys *ret_fs);

(style) align after '(

> diff --git a/misc/e2image.c b/misc/e2image.c
> index 727e3876..d8569068 100644
> --- a/misc/e2image.c
> +++ b/misc/e2image.c
> @@ -1612,6 +1612,13 @@ int main (int argc, char ** argv)
> 	sprintf(offset_opt, "offset=%llu", source_offset);
> 	retval = ext2fs_open2(device_name, offset_opt, open_flag,
> 			      superblock, blocksize, unix_io_manager, &fs);
> +	if (retval & (superblock | blocksize)) {
> +		printf(_("Try backups in other location.\n"));
> +		retval = try_backups(device_name, offset_opt, open_flag,
> +					    &superblock, &blocksize,
> +					    unix_io_manager, &fs);

(style) align after '('


Cheers, Andreas
Artem Blagodarenko June 5, 2018, 1:23 p.m. | #2
Hello Andreas,

This issue was discussed on weekly ext4 developer concall (can not remain the date, months ago). If I understood Theodore right way , moving this code out of exported from ext2fs library was the main requirements for this new feature to be accepted.  Theodore, I am right?

Thanks.
Artem Blagodarenko.

> On 4 Jun 2018, at 20:58, Andreas Dilger <adilger@dilger.ca> wrote:
> 
> On May 26, 2018, at 4:46 PM, Artem Blagodarenko <artem.blagodarenko@gmail.com> wrote:
>> 
>> e2image and e2fsck automatically try to open some backup superblocks,
>> if only blocksize is set or passed superblock can't be opened.
>> Try few backup superblocks (e.g. {1, 3, 5, 7, 9} * blocksize * 8).
>> 
>> This code is moved to lib/support/.
> 
> I don't recall seeing an explanation of why this is in lib/support/
> instead of lib/ext2fs?  Maybe as ext2fs_open_backups(), or is there
> some reason we don't want to export this function?
> 
>> Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com>
>> ---
>> 
>> diff --git a/e2fsck/message.c b/e2fsck/message.c
>> index 727f71d5..415d7609 100644
>> --- a/e2fsck/message.c
>> +++ b/e2fsck/message.c
>> @@ -465,7 +465,8 @@ static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs,
>> 		fprintf(f, "%*lld", width, (long long) ctx->blkcount);
>> 		break;
>> 	case 'S':
>> -		fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL));
>> +		fprintf(f, "%llu", get_first_backup_sb(NULL, NULL, fs,
>> +							  NULL, NULL));
> 
> (style) align after '('
> 
>> diff --git a/e2fsck/unix.c b/e2fsck/unix.c
>> index 491e9eb6..1a5e556f 100644
>> --- a/e2fsck/unix.c
>> +++ b/e2fsck/unix.c
>> @@ -1479,11 +1479,19 @@ restart:
>> 				retval ? _("Superblock invalid,") :
>> 				_("Group descriptors look bad..."));
>> 			orig_superblock = ctx->superblock;
>> -			get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
>> -			if (fs)
>> -				ext2fs_close_free(&fs);
>> 			orig_retval = retval;
>> -			retval = try_open_fs(ctx, flags, io_ptr, &fs);
>> +			retval = try_backups(ctx->filesystem_name,
>> +						    ctx->io_options,
>> +						    flags,
>> +						    &ctx->superblock,
>> +						    &ctx->blocksize, io_ptr,
>> +						    &fs);
> 
> (style) align after '(', pack onto fewest lines possible
> 
>> diff --git a/lib/support/sb_backup.c b/lib/support/sb_backup.c
>> new file mode 100644
>> index 00000000..5660e237
>> --- /dev/null
>> +++ b/lib/support/sb_backup.c
> 
> (style) remove extra blank lines
> 
>> +blk64_t get_first_backup_sb(blk64_t *superblock, unsigned int *block_size,
>> +			       ext2_filsys fs, const char *name,
>> +			       io_manager manager)
> 
> (style) align after '('
> 
>> +{
>> +	struct ext2_super_block *sb;
>> +	io_channel		io = NULL;
>> +	void			*buf = NULL;
>> +	int			try_blocksize;
>> +	blk64_t			try_superblock, ret_sb = 8193;
>> +
>> +	/* superblock and block_size can be NULL if fs->super is passed */
>> +	if (fs && fs->super) {
>> +		ret_sb = (fs->super->s_blocks_per_group +
>> +			  fs->super->s_first_data_block);
> 
> (style) no need for parenthesis here
> 
>> +		if (superblock)
>> +			*superblock = ret_sb;
>> +		if(block_size)
> 
> (style) space after "if"
> 
>> +}
>> +
>> +
> 
> (style) remove extra blank line
> 
>> +errcode_t try_backups(const char *name, const char *io_options,
>> +			    int flags, blk64_t *superblock,
>> +			    unsigned int *block_size, io_manager manager,
>> +			    ext2_filsys *ret_fs)
> 
> (style) align after '('
> 
>> +{
>> +	errcode_t retval;
>> +	blk64_t try_block_number;
>> +	unsigned int i;
>> +
>> +	/*
>> +	 * Get first superblock location based on heuristic.
>> +	 * Blocksize is also returned and used to find next
>> +	 * superblock copy location.
>> +	 */
>> +	try_block_number = get_first_backup_sb(superblock, block_size,
>> +						  *ret_fs, name, manager);
> 
> (style) align after '('
> 
>> +
>> +}
>> +
>> +
> 
> (style) remove blank lines at end
> 
>> diff --git a/lib/support/sb_backup.h b/lib/support/sb_backup.h
>> new file mode 100644
>> index 00000000..f1d48bff
>> --- /dev/null
>> +++ b/lib/support/sb_backup.h
>> @@ -0,0 +1,20 @@
>> +blk64_t get_first_backup_sb(blk64_t *superblock, unsigned int *block_size,
>> +			       ext2_filsys fs, const char *name,
>> +			       io_manager manager);
>> +
>> +errcode_t try_backups(const char *name, const char *io_options,
>> +			    int flags, blk64_t *superblock,
>> +			    unsigned int *block_size, io_manager manager,
>> +			    ext2_filsys *ret_fs);
> 
> (style) align after '(
> 
>> diff --git a/misc/e2image.c b/misc/e2image.c
>> index 727e3876..d8569068 100644
>> --- a/misc/e2image.c
>> +++ b/misc/e2image.c
>> @@ -1612,6 +1612,13 @@ int main (int argc, char ** argv)
>> 	sprintf(offset_opt, "offset=%llu", source_offset);
>> 	retval = ext2fs_open2(device_name, offset_opt, open_flag,
>> 			      superblock, blocksize, unix_io_manager, &fs);
>> +	if (retval & (superblock | blocksize)) {
>> +		printf(_("Try backups in other location.\n"));
>> +		retval = try_backups(device_name, offset_opt, open_flag,
>> +					    &superblock, &blocksize,
>> +					    unix_io_manager, &fs);
> 
> (style) align after '('
> 
> 
> Cheers, Andreas
> 
> 
> 
> 
>

Patch

diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 5269650f..ec7e899a 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -630,8 +630,6 @@  extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
 #ifdef MTRACE
 extern void mtrace_print(char *mesg);
 #endif
-extern blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
-			   const char *name, io_manager manager);
 extern int ext2_file_type(unsigned int mode);
 extern int write_all(int fd, char *buf, size_t count);
 void dump_mmp_msg(struct mmp_struct *mmp, const char *msg);
diff --git a/e2fsck/message.c b/e2fsck/message.c
index 727f71d5..415d7609 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -465,7 +465,8 @@  static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs,
 		fprintf(f, "%*lld", width, (long long) ctx->blkcount);
 		break;
 	case 'S':
-		fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL));
+		fprintf(f, "%llu", get_first_backup_sb(NULL, NULL, fs,
+							  NULL, NULL));
 		break;
 	case 's':
 		fprintf(f, "%*s", width, ctx->str ? ctx->str : "NULL");
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index 491e9eb6..1a5e556f 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -1479,11 +1479,19 @@  restart:
 				retval ? _("Superblock invalid,") :
 				_("Group descriptors look bad..."));
 			orig_superblock = ctx->superblock;
-			get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
-			if (fs)
-				ext2fs_close_free(&fs);
 			orig_retval = retval;
-			retval = try_open_fs(ctx, flags, io_ptr, &fs);
+			retval = try_backups(ctx->filesystem_name,
+						    ctx->io_options,
+						    flags,
+						    &ctx->superblock,
+						    &ctx->blocksize, io_ptr,
+						    &fs);
+			if (retval == 0) {
+				fs->priv_data = ctx;
+				e2fsck_set_bitmap_type(fs,
+						       EXT2FS_BMAP64_RBTREE,
+						       "default", NULL);
+			}
 			if ((orig_retval == 0) && retval != 0) {
 				if (fs)
 					ext2fs_close_free(&fs);
diff --git a/e2fsck/util.c b/e2fsck/util.c
index ed947025..b1c638c5 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -548,79 +548,6 @@  void mtrace_print(char *mesg)
 }
 #endif
 
-blk64_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
-		      io_manager manager)
-{
-	struct ext2_super_block *sb;
-	io_channel		io = NULL;
-	void			*buf = NULL;
-	int			blocksize;
-	blk64_t			superblock, ret_sb = 8193;
-
-	if (fs && fs->super) {
-		ret_sb = (fs->super->s_blocks_per_group +
-			  fs->super->s_first_data_block);
-		if (ctx) {
-			ctx->superblock = ret_sb;
-			ctx->blocksize = fs->blocksize;
-		}
-		return ret_sb;
-	}
-
-	if (ctx) {
-		if (ctx->blocksize) {
-			ret_sb = ctx->blocksize * 8;
-			if (ctx->blocksize == 1024)
-				ret_sb++;
-			ctx->superblock = ret_sb;
-			return ret_sb;
-		}
-		ctx->superblock = ret_sb;
-		ctx->blocksize = 1024;
-	}
-
-	if (!name || !manager)
-		goto cleanup;
-
-	if (manager->open(name, 0, &io) != 0)
-		goto cleanup;
-
-	if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
-		goto cleanup;
-	sb = (struct ext2_super_block *) buf;
-
-	for (blocksize = EXT2_MIN_BLOCK_SIZE;
-	     blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
-		superblock = blocksize*8;
-		if (blocksize == 1024)
-			superblock++;
-		io_channel_set_blksize(io, blocksize);
-		if (io_channel_read_blk64(io, superblock,
-					-SUPERBLOCK_SIZE, buf))
-			continue;
-#ifdef WORDS_BIGENDIAN
-		if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
-			ext2fs_swap_super(sb);
-#endif
-		if ((sb->s_magic == EXT2_SUPER_MAGIC) &&
-		    (EXT2_BLOCK_SIZE(sb) == blocksize)) {
-			ret_sb = superblock;
-			if (ctx) {
-				ctx->superblock = superblock;
-				ctx->blocksize = blocksize;
-			}
-			break;
-		}
-	}
-
-cleanup:
-	if (io)
-		io_channel_close(io);
-	if (buf)
-		ext2fs_free_mem(&buf);
-	return (ret_sb);
-}
-
 /*
  * Given a mode, return the ext2 file type
  */
diff --git a/lib/support/Makefile.in b/lib/support/Makefile.in
index 40206b74..d5f2fc30 100644
--- a/lib/support/Makefile.in
+++ b/lib/support/Makefile.in
@@ -22,7 +22,8 @@  OBJS=		cstring.o \
 		quotaio.o \
 		quotaio_v2.o \
 		quotaio_tree.o \
-		dict.o
+		dict.o \
+		sb_backup.o
 
 SRCS=		$(srcdir)/argv_parse.c \
 		$(srcdir)/cstring.c \
@@ -35,7 +36,8 @@  SRCS=		$(srcdir)/argv_parse.c \
 		$(srcdir)/quotaio.c \
 		$(srcdir)/quotaio_tree.c \
 		$(srcdir)/quotaio_v2.c \
-		$(srcdir)/dict.c
+		$(srcdir)/dict.c \
+		$(srcdir)/sb_backup.c
 
 LIBRARY= libsupport
 LIBDIR= support
@@ -165,3 +167,5 @@  quotaio_v2.o: $(srcdir)/quotaio_v2.c $(top_builddir)/lib/config.h \
  $(srcdir)/dqblk_v2.h $(srcdir)/quotaio_tree.h
 dict.o: $(srcdir)/dict.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/dict.h
+sb_backup.o: $(srcdir)/sb_backup.c $(top_builddir)/lib/config.h \
+ $(srcdir)/sb_backup.h
diff --git a/lib/support/sb_backup.c b/lib/support/sb_backup.c
new file mode 100644
index 00000000..5660e237
--- /dev/null
+++ b/lib/support/sb_backup.c
@@ -0,0 +1,137 @@ 
+/*
+ * sb_backup.c -- helper functions for getting backup superblocks 
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+
+
+
+blk64_t get_first_backup_sb(blk64_t *superblock, unsigned int *block_size,
+			       ext2_filsys fs, const char *name,
+			       io_manager manager)
+{
+	struct ext2_super_block *sb;
+	io_channel		io = NULL;
+	void			*buf = NULL;
+	int			try_blocksize;
+	blk64_t			try_superblock, ret_sb = 8193;
+
+	/* superblock and block_size can be NULL if fs->super is passed */
+	if (fs && fs->super) {
+		ret_sb = (fs->super->s_blocks_per_group +
+			  fs->super->s_first_data_block);
+		if (superblock)
+			*superblock = ret_sb;
+		if(block_size)
+			*block_size = fs->blocksize;
+		return ret_sb;
+	}
+
+	if (*block_size) {
+		ret_sb = *block_size * 8;
+		if (*block_size == 1024)
+			ret_sb++;
+		*superblock = ret_sb;
+		return ret_sb;
+	}
+
+	*superblock = ret_sb;
+	*block_size = 1024;
+
+	if (!name || !manager)
+		goto cleanup;
+
+	if (manager->open(name, 0, &io) != 0)
+		goto cleanup;
+
+	if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
+		goto cleanup;
+	sb = (struct ext2_super_block *) buf;
+
+	for (try_blocksize = EXT2_MIN_BLOCK_SIZE;
+	     try_blocksize <= EXT2_MAX_BLOCK_SIZE ; try_blocksize *= 2) {
+		try_superblock = try_blocksize*8;
+		if (try_blocksize == 1024)
+			try_superblock++;
+		io_channel_set_blksize(io, try_blocksize);
+		if (io_channel_read_blk64(io, try_superblock,
+					-SUPERBLOCK_SIZE, buf))
+			continue;
+#ifdef WORDS_BIGENDIAN
+		if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
+			ext2fs_swap_super(sb);
+#endif
+		if ((sb->s_magic == EXT2_SUPER_MAGIC) &&
+		    (EXT2_BLOCK_SIZE(sb) == try_blocksize)) {
+			ret_sb = try_superblock;
+			*superblock = try_superblock;
+			*block_size = try_blocksize;
+			break;
+		}
+	}
+
+cleanup:
+	if (io)
+		io_channel_close(io);
+	if (buf)
+		ext2fs_free_mem(&buf);
+	return ret_sb;
+}
+
+
+errcode_t try_backups(const char *name, const char *io_options,
+			    int flags, blk64_t *superblock,
+			    unsigned int *block_size, io_manager manager,
+			    ext2_filsys *ret_fs)
+{
+	errcode_t retval;
+	blk64_t try_block_number;
+	unsigned int i;
+
+	/*
+	 * Get first superblock location based on heuristic.
+	 * Blocksize is also returned and used to find next
+	 * superblock copy location.
+	 */
+	try_block_number = get_first_backup_sb(superblock, block_size,
+						  *ret_fs, name, manager);
+	retval = ext2fs_open2(name, io_options, flags, try_block_number,
+			      *block_size, manager, ret_fs);
+	if (!retval)
+		return 0;
+
+	/*
+	 * Try 3d, 5th, 7th, 9th superblock copy
+	 */
+	for (i = 3; i <= 9; i += 2) {
+		try_block_number = (i * (*block_size) * 8);
+		if (*block_size == 1024)
+			try_block_number++;
+		if (*ret_fs) {
+			ext2fs_free(*ret_fs);
+			*ret_fs = NULL;
+		}
+		retval = ext2fs_open2(name, io_options, flags,
+				      try_block_number, *block_size, manager,
+				      ret_fs);
+		if (!retval) {
+			*superblock = try_block_number;
+			break;
+		}
+	}
+
+	return retval;
+}
+
+
diff --git a/lib/support/sb_backup.h b/lib/support/sb_backup.h
new file mode 100644
index 00000000..f1d48bff
--- /dev/null
+++ b/lib/support/sb_backup.h
@@ -0,0 +1,20 @@ 
+/*
+ * sb_backup.h -- helper functions for getting backup superblocks
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+
+blk64_t get_first_backup_sb(blk64_t *superblock, unsigned int *block_size,
+			       ext2_filsys fs, const char *name,
+			       io_manager manager);
+
+errcode_t try_backups(const char *name, const char *io_options,
+			    int flags, blk64_t *superblock,
+			    unsigned int *block_size, io_manager manager,
+			    ext2_filsys *ret_fs);
diff --git a/misc/e2image.c b/misc/e2image.c
index 727e3876..d8569068 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -1612,6 +1612,13 @@  int main (int argc, char ** argv)
 	sprintf(offset_opt, "offset=%llu", source_offset);
 	retval = ext2fs_open2(device_name, offset_opt, open_flag,
 			      superblock, blocksize, unix_io_manager, &fs);
+	if (retval & (superblock | blocksize)) {
+		printf(_("Try backups in other location.\n"));
+		retval = try_backups(device_name, offset_opt, open_flag,
+					    &superblock, &blocksize,
+					    unix_io_manager, &fs);
+		printf(_("Use superblock %i.\n"), superblock);
+	}
         if (retval) {
 		com_err (program_name, retval, _("while trying to open %s"),
 			 device_name);