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
26unsigned char mtime_key[] = "filesystem MTIME";
27unsigned char uuid_key[] = "filesystem UUID";
28unsigned char blksize_key[] = "filesystem BLKSIZE";
29
30char *prg_name;
31
32static void usage(char *prg_name)
33{
34	fprintf(stderr,
35		_("Usage: %s <transaction file> <filesystem>\n"), prg_name);
36	exit(1);
37
38}
39
40static int check_filesystem(TDB_CONTEXT *tdb, io_channel channel)
41{
42	__u32   s_mtime;
43	__u8    s_uuid[16];
44	errcode_t retval;
45	TDB_DATA tdb_key, tdb_data;
46	struct ext2_super_block super;
47
48	io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
49	retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super);
50	if (retval) {
51		com_err(prg_name,
52			retval, _("Failed to read the file system data \n"));
53		return retval;
54	}
55
56	tdb_key.dptr = mtime_key;
57	tdb_key.dsize = sizeof(mtime_key);
58	tdb_data = tdb_fetch(tdb, tdb_key);
59	if (!tdb_data.dptr) {
60		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
61		com_err(prg_name, retval,
62			_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
63		return retval;
64	}
65
66	s_mtime = *(__u32 *)tdb_data.dptr;
67	if (super.s_mtime != s_mtime) {
68
69		com_err(prg_name, 0,
70			_("The file system Mount time didn't match %u\n"),
71			s_mtime);
72
73		return  -1;
74	}
75
76
77	tdb_key.dptr = uuid_key;
78	tdb_key.dsize = sizeof(uuid_key);
79	tdb_data = tdb_fetch(tdb, tdb_key);
80	if (!tdb_data.dptr) {
81		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
82		com_err(prg_name, retval,
83			_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
84		return retval;
85	}
86	memcpy(s_uuid, tdb_data.dptr, sizeof(s_uuid));
87	if (memcmp(s_uuid, super.s_uuid, sizeof(s_uuid))) {
88		com_err(prg_name, 0,
89			_("The file system UUID didn't match \n"));
90		return -1;
91	}
92
93	return 0;
94}
95
96static int set_blk_size(TDB_CONTEXT *tdb, io_channel channel)
97{
98	int block_size;
99	errcode_t retval;
100	TDB_DATA tdb_key, tdb_data;
101
102	tdb_key.dptr = blksize_key;
103	tdb_key.dsize = sizeof(blksize_key);
104	tdb_data = tdb_fetch(tdb, tdb_key);
105	if (!tdb_data.dptr) {
106		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
107		com_err(prg_name, retval,
108			_("Failed tdb_fetch %s\n"), tdb_errorstr(tdb));
109		return retval;
110	}
111
112	block_size = *(int *)tdb_data.dptr;
113#ifdef DEBUG
114	printf("Block size %d\n", block_size);
115#endif
116	io_channel_set_blksize(channel, block_size);
117
118	return 0;
119}
120
121int main(int argc, char *argv[])
122{
123	int c,force = 0;
124	TDB_CONTEXT *tdb;
125	TDB_DATA key, data;
126	io_channel channel;
127	errcode_t retval;
128	int  mount_flags;
129	unsigned long  blk_num;
130	char *device_name, *tdb_file;
131	io_manager manager = unix_io_manager;
132
133#ifdef ENABLE_NLS
134	setlocale(LC_MESSAGES, "");
135	setlocale(LC_CTYPE, "");
136	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
137	textdomain(NLS_CAT_NAME);
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(prg_name);
149		}
150	}
151
152	if (argc != optind+2)
153		usage(prg_name);
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, _("e2undo should only be run on "
175				"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 = *(unsigned long *)key.dptr;
209		printf(_("Replayed transaction of size %zd at location %ld\n"),
210							data.dsize, blk_num);
211		retval = io_channel_write_blk(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