/* * 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" static unsigned char mtime_key[] = "filesystem MTIME"; static unsigned char uuid_key[] = "filesystem UUID"; static unsigned char blksize_key[] = "filesystem BLKSIZE"; static char *prg_name; static void usage(void) { 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_blk64(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) { com_err(prg_name, retval, "%s", _("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(prg_name, 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(prg_name, 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(prg_name, 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(prg_name, 0, "%s", _("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(prg_name, 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; blk64_t blk_num; char *device_name, *tdb_file; io_manager manager = unix_io_manager; #ifdef ENABLE_NLS setlocale(LC_MESSAGES, ""); setlocale(LC_CTYPE, ""); bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); set_com_err_gettext(gettext); #endif add_error_table(&et_ext2_error_table); prg_name = argv[0]; while((c = getopt(argc, argv, "f")) != EOF) { switch (c) { case 'f': force = 1; break; default: usage(); } } if (argc != optind + 2) usage(); 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, "%s", _("e2undo 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 = *(blk64_t *)key.dptr; printf(_("Replayed transaction of size %zd at location %llu\n"), data.dsize, blk_num); retval = io_channel_write_blk64(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); return 0; }