Whamcloud - gitweb
Add e2undo command
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Mon, 13 Aug 2007 10:26:22 +0000 (15:56 +0530)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 27 Apr 2008 23:42:05 +0000 (19:42 -0400)
The e2undo command can be used to replay the transaction saved in the
transaction file using undo I/O Manager.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
misc/Makefile.in
misc/e2undo.8.in [new file with mode: 0644]
misc/e2undo.c [new file with mode: 0644]

index bf04e65..b31f5c3 100644 (file)
@@ -18,11 +18,11 @@ INSTALL = @INSTALL@
 @UUIDD_CMT@UUIDD_MAN= uuidd.8
 
 SPROGS=                mke2fs badblocks tune2fs dumpe2fs blkid logsave \
-                       $(E2IMAGE_PROG) @FSCK_PROG@
+                       $(E2IMAGE_PROG) @FSCK_PROG@ e2undo
 USPROGS=       mklost+found filefrag $(UUIDD_PROG)
 SMANPAGES=     tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
                        e2label.8 findfs.8 blkid.8 $(E2IMAGE_MAN) \
-                       logsave.8 filefrag.8 $(UUIDD_MAN) @FSCK_MAN@
+                       logsave.8 filefrag.8 e2undo.8 $(UUIDD_MAN) @FSCK_MAN@
 FMANPAGES=     mke2fs.conf.5
 
 UPROGS=                chattr lsattr uuidgen
@@ -43,6 +43,7 @@ E2IMAGE_OBJS= e2image.o
 FSCK_OBJS=     fsck.o base_device.o ismounted.o
 BLKID_OBJS=    blkid.o
 FILEFRAG_OBJS= filefrag.o
+E2UNDO_OBJS=  e2undo.o
 
 XTRA_CFLAGS=   -I$(srcdir)/../e2fsck -I.
 
@@ -51,7 +52,8 @@ SRCS= $(srcdir)/tune2fs.c $(srcdir)/mklost+found.c $(srcdir)/mke2fs.c \
                $(srcdir)/badblocks.c $(srcdir)/fsck.c $(srcdir)/util.c \
                $(srcdir)/uuidgen.c $(srcdir)/blkid.c $(srcdir)/logsave.c \
                $(srcdir)/filefrag.c $(srcdir)/base_device.c \
-               $(srcdir)/ismounted.c $(srcdir)/../e2fsck/profile.c
+               $(srcdir)/ismounted.c $(srcdir)/../e2fsck/profile.c \
+               $(srcdir)/e2undo.c
 
 LIBS= $(LIBEXT2FS) $(LIBCOM_ERR) 
 DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR) 
@@ -112,6 +114,10 @@ e2image: $(E2IMAGE_OBJS) $(DEPLIBS)
        @echo " LD $@"
        @$(CC) $(ALL_LDFLAGS) -o e2image $(E2IMAGE_OBJS) $(LIBS) $(LIBINTL)
 
+e2undo: $(E2UNDO_OBJS) $(DEPLIBS)
+       @echo " LD $@"
+       @$(CC) $(ALL_LDFLAGS) -o e2undo $(E2UNDO_OBJS) $(LIBS)
+
 base_device: base_device.c
        @echo " LD $@"
        @$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(srcdir)/base_device.c \
@@ -197,6 +203,10 @@ e2label.8: $(DEP_SUBSTITUTE) $(srcdir)/e2label.8.in
        @echo " SUBST $@"
        @$(SUBSTITUTE_UPTIME) $(srcdir)/e2label.8.in e2label.8
 
+e2undo.8: $(DEP_SUBSTITUTE) $(srcdir)/e2undo.8.in
+       @echo " SUBST $@"
+       @$(SUBSTITUTE_UPTIME) $(srcdir)/e2undo.8.in e2undo.8
+
 findfs.8: $(DEP_SUBSTITUTE) $(srcdir)/findfs.8.in
        @echo " SUBST $@"
        @$(SUBSTITUTE_UPTIME) $(srcdir)/findfs.8.in findfs.8
@@ -451,3 +461,4 @@ base_device.o: $(srcdir)/base_device.c $(srcdir)/fsck.h
 ismounted.o: $(srcdir)/ismounted.c $(top_srcdir)/lib/et/com_err.h
 profile.o: $(srcdir)/../e2fsck/profile.c $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/../e2fsck/profile.h prof_err.h
+e2undo.o: $(srcdir)/e2undo.c $(top_srcdir)/lib/ext2fs/tdb.h
diff --git a/misc/e2undo.8.in b/misc/e2undo.8.in
new file mode 100644 (file)
index 0000000..4bf0798
--- /dev/null
@@ -0,0 +1,44 @@
+.\" -*- nroff -*-
+.\" Copyright 2008 by Theodore Ts'o.  All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH E2UNDO 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+e2undo \- Replay an undo log for an ext2/ext3/ext4 filesystem
+.SH SYNOPSIS
+.B e2undo
+[
+.B \-f
+]
+.I undo_log device
+.SH DESCRIPTION
+.B e2undo
+will replay the undo log
+.I undo_log
+for an ext2/ext3/ext4 filesystem found on
+.IR device .
+This can be
+used to undo a failed operation by an e2fsprogs program.
+.SH OPTIONS
+.TP
+.B \-f
+Normally,
+.B e2undo
+will check the filesystem UUID and last modified time to make sure the
+undo log matches with the filesystem on the device.  If they do not
+match,
+.B e2undo
+will refuse to apply the undo log as a safety mechanism.  The
+.B \-f
+option disables this safety mechanism.
+.SH AUTHOR
+.B e2undo
+was written by Aneesh Kumar K.V. (aneesh.kumar@linux.vnet.ibm.com)
+.SH AVAILABILITY
+.B e2undo
+is part of the e2fsprogs package and is available from
+http://e2fsprogs.sourceforge.net.
+.SH SEE ALSO
+.BR mke2fs (8),
+.BR tune2fs (8)
+
diff --git a/misc/e2undo.c b/misc/e2undo.c
new file mode 100644 (file)
index 0000000..ca24303
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * e2undo.c - Replay an undo log onto an ext2/3/4 filesystem
+ *
+ * Copyright IBM Corporation, 2007
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <fcntl.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "ext2fs/tdb.h"
+#include "ext2fs/ext2fs.h"
+#include "nls-enable.h"
+
+unsigned char mtime_key[] = "filesystem MTIME";
+unsigned char uuid_key[] = "filesystem UUID";
+unsigned char blksize_key[] = "filesystem BLKSIZE";
+
+static void usage(char *prg_name)
+{
+       fprintf(stderr,
+               _("Usage: %s <transaction file> <filesystem>\n"), prg_name);
+       exit(1);
+
+}
+
+static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel)
+{
+       __u32   s_mtime;
+       __u8    s_uuid[16];
+       errcode_t retval;
+       TDB_DATA tdb_key, tdb_data;
+       struct ext2_super_block super;
+
+       io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
+       retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super);
+       if (retval) {
+               com_err(__FUNCTION__,
+                       retval, _("Failed to read the file system data \n"));
+               return retval;
+       }
+
+       tdb_key.dptr = mtime_key;
+       tdb_key.dsize = sizeof(mtime_key);
+       tdb_data = tdb_fetch(tdb, tdb_key);
+       if (!tdb_data.dptr) {
+               retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
+               com_err(__FUNCTION__, retval,
+                       _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
+               return retval;
+       }
+
+       s_mtime = *(__u32 *)tdb_data.dptr;
+       if (super.s_mtime != s_mtime) {
+
+               com_err(__FUNCTION__, 0,
+                       _("The file system Mount time didn't match %u\n"),
+                       s_mtime);
+
+               return  -1;
+       }
+
+
+       tdb_key.dptr = uuid_key;
+       tdb_key.dsize = sizeof(uuid_key);
+       tdb_data = tdb_fetch(tdb, tdb_key);
+       if (!tdb_data.dptr) {
+               retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
+               com_err(__FUNCTION__, retval,
+                       _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
+               return retval;
+       }
+       memcpy(s_uuid, tdb_data.dptr, sizeof(s_uuid));
+       if (memcmp(s_uuid, super.s_uuid, sizeof(s_uuid))) {
+               com_err(__FUNCTION__, 0,
+                       _("The file system UUID didn't match \n"));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int set_blk_size(TDB_CONTEXT *tdb, io_channel channel)
+{
+       int block_size;
+       errcode_t retval;
+       TDB_DATA tdb_key, tdb_data;
+
+       tdb_key.dptr = blksize_key;
+       tdb_key.dsize = sizeof(blksize_key);
+       tdb_data = tdb_fetch(tdb, tdb_key);
+       if (!tdb_data.dptr) {
+               retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
+               com_err(__FUNCTION__, retval,
+                       _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
+               return retval;
+       }
+
+       block_size = *(int *)tdb_data.dptr;
+#ifdef DEBUG
+       printf("Block size %d\n", block_size);
+#endif
+       io_channel_set_blksize(channel, block_size);
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int c,force = 0;
+       TDB_CONTEXT *tdb;
+       TDB_DATA key, data;
+       io_channel channel;
+       errcode_t retval;
+       int  mount_flags;
+       unsigned long  blk_num;
+       char *device_name, *tdb_file, *prg_name;
+       io_manager manager = unix_io_manager;
+
+       prg_name = argv[0];
+       while((c = getopt(argc, argv, "f")) != EOF) {
+               switch (c) {
+                       case 'f':
+                               force = 1;
+                               break;
+                       default:
+                               usage(prg_name);
+               }
+       }
+
+       if (argc != optind+2)
+               usage(prg_name);
+
+       tdb_file = argv[optind];
+       device_name = argv[optind+1];
+
+       tdb = tdb_open(tdb_file, 0, 0, O_RDONLY, 0600);
+
+       if (!tdb) {
+               com_err(prg_name, errno,
+                               _("Failed tdb_open %s\n"), tdb_file);
+               exit(1);
+       }
+
+       retval = ext2fs_check_if_mounted(device_name, &mount_flags);
+       if (retval) {
+               com_err(prg_name, retval, _("Error while determining whether "
+                               "%s is mounted.\n"), device_name);
+               exit(1);
+       }
+
+       if (mount_flags & EXT2_MF_MOUNTED) {
+               com_err(prg_name, retval, _("undoe2fs should only be run on "
+                               "unmounted file system\n"));
+               exit(1);
+       }
+
+       retval = manager->open(device_name,
+                               IO_FLAG_EXCLUSIVE | IO_FLAG_RW,  &channel);
+       if (retval) {
+               com_err(prg_name, retval,
+                               _("Failed to open %s\n"), device_name);
+               exit(1);
+       }
+
+       if (!force && check_filesystem(tdb, channel)) {
+               exit(1);
+       }
+
+       if (set_blk_size(tdb, channel)) {
+               exit(1);
+       }
+
+       for (key = tdb_firstkey(tdb); key.dptr; key = tdb_nextkey(tdb, key)) {
+               if (!strcmp((char *) key.dptr, (char *) mtime_key) ||
+                   !strcmp((char *) key.dptr, (char *) uuid_key) ||
+                   !strcmp((char *) key.dptr, (char *) blksize_key)) {
+                       continue;
+               }
+
+               data = tdb_fetch(tdb, key);
+               if (!data.dptr) {
+                       com_err(prg_name, 0,
+                               _("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
+                       exit(1);
+               }
+               blk_num = *(unsigned long *)key.dptr;
+               printf(_("Replayed transaction of size %d at location %ld\n"),
+                                                       data.dsize, blk_num);
+               retval = io_channel_write_blk(channel, blk_num,
+                                               -data.dsize, data.dptr);
+               if (retval == -1) {
+                       com_err(prg_name, retval,
+                                       _("Failed write %s\n"),
+                                       strerror(errno));
+                       exit(1);
+               }
+       }
+       io_channel_close(channel);
+       tdb_close(tdb);
+
+}