1/*
2 * e2undo.c - Replay an undo log onto an ext2/3/4 filesystem
3 *
4 * Copyright IBM Corporation, 2007
5 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#ifdef HAVE_GETOPT_H
16#include <getopt.h>
17#endif
18#include <fcntl.h>
19#if HAVE_ERRNO_H
20#include <errno.h>
21#endif
22#include "ext2fs/tdb.h"
23#include "ext2fs/ext2fs.h"
24#include "nls-enable.h"
25
26static unsigned char mtime_key[] = "filesystem MTIME";
27static unsigned char uuid_key[] = "filesystem UUID";
28static unsigned char blksize_key[] = "filesystem BLKSIZE";
29
30static char *prg_name;
31
32static void usage(void)
33{
34	fprintf(stderr,
35		_("Usage: %s <transaction file> <filesystem>\n"), prg_name);
36	exit(1);
37}
38
39static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel)
40{
41	__u32   s_mtime;
42	__u8    s_uuid[16];
43	errcode_t retval;
44	TDB_DATA tdb_key, tdb_data;
45	struct ext2_super_block super;
46
47	io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
48	retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
49	if (retval) {
50		com_err(prg_name, retval,
51			"%s", _("Failed to read the file system data \n"));
52		return retval;
53	}
54
55	tdb_key.dptr = mtime_key;
56	tdb_key.dsize = sizeof(mtime_key);
57	tdb_data = tdb_fetch(tdb, tdb_key);
58	if (!tdb_data.dptr) {
59		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
60		com_err(prg_name, retval,
61			_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
62		return retval;
63	}
64
65	s_mtime = *(__u32 *)tdb_data.dptr;
66	if (super.s_mtime != s_mtime) {
67
68		com_err(prg_name, 0,
69			_("The file system Mount time didn't match %u\n"),
70			s_mtime);
71
72		return  -1;
73	}
74
75
76	tdb_key.dptr = uuid_key;
77	tdb_key.dsize = sizeof(uuid_key);
78	tdb_data = tdb_fetch(tdb, tdb_key);
79	if (!tdb_data.dptr) {
80		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
81		com_err(prg_name, retval,
82			_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
83		return retval;
84	}
85	memcpy(s_uuid, tdb_data.dptr, sizeof(s_uuid));
86	if (memcmp(s_uuid, super.s_uuid, sizeof(s_uuid))) {
87		com_err(prg_name, 0, "%s",
88			_("The file system UUID didn't match \n"));
89		return -1;
90	}
91
92	return 0;
93}
94
95static int set_blk_size(TDB_CONTEXT *tdb, io_channel channel)
96{
97	int block_size;
98	errcode_t retval;
99	TDB_DATA tdb_key, tdb_data;
100
101	tdb_key.dptr = blksize_key;
102	tdb_key.dsize = sizeof(blksize_key);
103	tdb_data = tdb_fetch(tdb, tdb_key);
104	if (!tdb_data.dptr) {
105		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
106		com_err(prg_name, retval,
107			_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
108		return retval;
109	}
110
111	block_size = *(int *)tdb_data.dptr;
112#ifdef DEBUG
113	printf("Block size %d\n", block_size);
114#endif
115	io_channel_set_blksize(channel, block_size);
116
117	return 0;
118}
119
120int main(int argc, char *argv[])
121{
122	int c,force = 0;
123	TDB_CONTEXT *tdb;
124	TDB_DATA key, data;
125	io_channel channel;
126	errcode_t retval;
127	int  mount_flags;
128	blk64_t  blk_num;
129	char *device_name, *tdb_file;
130	io_manager manager = unix_io_manager;
131
132#ifdef ENABLE_NLS
133	setlocale(LC_MESSAGES, "");
134	setlocale(LC_CTYPE, "");
135	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
136	textdomain(NLS_CAT_NAME);
137	set_com_err_gettext(gettext);
138#endif
139	add_error_table(&et_ext2_error_table);
140
141	prg_name = argv[0];
142	while((c = getopt(argc, argv, "f")) != EOF) {
143		switch (c) {
144			case 'f':
145				force = 1;
146				break;
147			default:
148				usage();
149		}
150	}
151
152	if (argc != optind + 2)
153		usage();
154
155	tdb_file = argv[optind];
156	device_name = argv[optind+1];
157
158	tdb = tdb_open(tdb_file, 0, 0, O_RDONLY, 0600);
159
160	if (!tdb) {
161		com_err(prg_name, errno,
162				_("Failed tdb_open %s\n"), tdb_file);
163		exit(1);
164	}
165
166	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
167	if (retval) {
168		com_err(prg_name, retval, _("Error while determining whether "
169				"%s is mounted.\n"), device_name);
170		exit(1);
171	}
172
173	if (mount_flags & EXT2_MF_MOUNTED) {
174		com_err(prg_name, retval, "%s", _("e2undo should only be run "
175						"on unmounted file system\n"));
176		exit(1);
177	}
178
179	retval = manager->open(device_name,
180				IO_FLAG_EXCLUSIVE | IO_FLAG_RW,  &channel);
181	if (retval) {
182		com_err(prg_name, retval,
183				_("Failed to open %s\n"), device_name);
184		exit(1);
185	}
186
187	if (!force && check_filesystem(tdb, channel)) {
188		exit(1);
189	}
190
191	if (set_blk_size(tdb, channel)) {
192		exit(1);
193	}
194
195	for (key = tdb_firstkey(tdb); key.dptr; key = tdb_nextkey(tdb, key)) {
196		if (!strcmp((char *) key.dptr, (char *) mtime_key) ||
197		    !strcmp((char *) key.dptr, (char *) uuid_key) ||
198		    !strcmp((char *) key.dptr, (char *) blksize_key)) {
199			continue;
200		}
201
202		data = tdb_fetch(tdb, key);
203		if (!data.dptr) {
204			com_err(prg_name, 0,
205				_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
206			exit(1);
207		}
208		blk_num = *(blk64_t *)key.dptr;
209		printf(_("Replayed transaction of size %zd at location %llu\n"),
210							data.dsize, blk_num);
211		retval = io_channel_write_blk64(channel, blk_num,
212						-data.dsize, data.dptr);
213		if (retval == -1) {
214			com_err(prg_name, retval,
215					_("Failed write %s\n"),
216					strerror(errno));
217			exit(1);
218		}
219	}
220	io_channel_close(channel);
221	tdb_close(tdb);
222
223	return 0;
224}
225