| /* |
| * 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"; |
| |
| char *prg_name; |
| |
| 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(prg_name, |
| 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(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, |
| _("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; |
| unsigned long 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); |
| #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(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, _("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 = *(unsigned long *)key.dptr; |
| printf(_("Replayed transaction of size %zd 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); |
| |
| return 0; |
| } |