undo_io.c revision 18a1444b4f1e6a0948fd38fa0de382d86cfe04de
1/*
2 * undo_io.c --- This is the undo io manager that copies the old data that
3 * copies the old data being overwritten into a tdb database
4 *
5 * Copyright IBM Corporation, 2007
6 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
7 *
8 * %Begin-Header%
9 * This file may be redistributed under the terms of the GNU Library
10 * General Public License, version 2.
11 * %End-Header%
12 */
13
14#define _LARGEFILE_SOURCE
15#define _LARGEFILE64_SOURCE
16
17#include <stdio.h>
18#include <string.h>
19#if HAVE_UNISTD_H
20#include <unistd.h>
21#endif
22#if HAVE_ERRNO_H
23#include <errno.h>
24#endif
25#include <fcntl.h>
26#include <time.h>
27#ifdef __linux__
28#include <sys/utsname.h>
29#endif
30#if HAVE_SYS_STAT_H
31#include <sys/stat.h>
32#endif
33#if HAVE_SYS_TYPES_H
34#include <sys/types.h>
35#endif
36#if HAVE_SYS_RESOURCE_H
37#include <sys/resource.h>
38#endif
39
40#include "tdb.h"
41
42#include "ext2_fs.h"
43#include "ext2fs.h"
44
45#ifdef __GNUC__
46#define ATTR(x) __attribute__(x)
47#else
48#define ATTR(x)
49#endif
50
51/*
52 * For checking structure magic numbers...
53 */
54
55#define EXT2_CHECK_MAGIC(struct, code) \
56	  if ((struct)->magic != (code)) return (code)
57
58struct undo_private_data {
59	int	magic;
60	TDB_CONTEXT *tdb;
61	char *tdb_file;
62
63	/* The backing io channel */
64	io_channel real;
65
66	int tdb_data_size;
67	int tdb_written;
68
69	/* to support offset in unix I/O manager */
70	ext2_loff_t offset;
71};
72
73static errcode_t undo_open(const char *name, int flags, io_channel *channel);
74static errcode_t undo_close(io_channel channel);
75static errcode_t undo_set_blksize(io_channel channel, int blksize);
76static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
77				 int count, void *data);
78static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
79				  int count, const void *data);
80static errcode_t undo_read_blk(io_channel channel, unsigned long block,
81			       int count, void *data);
82static errcode_t undo_write_blk(io_channel channel, unsigned long block,
83				int count, const void *data);
84static errcode_t undo_flush(io_channel channel);
85static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
86				int size, const void *data);
87static errcode_t undo_set_option(io_channel channel, const char *option,
88				 const char *arg);
89static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
90
91static struct struct_io_manager struct_undo_manager = {
92	EXT2_ET_MAGIC_IO_MANAGER,
93	"Undo I/O Manager",
94	undo_open,
95	undo_close,
96	undo_set_blksize,
97	undo_read_blk,
98	undo_write_blk,
99	undo_flush,
100	undo_write_byte,
101	undo_set_option,
102	undo_get_stats,
103	undo_read_blk64,
104	undo_write_blk64,
105};
106
107io_manager undo_io_manager = &struct_undo_manager;
108static io_manager undo_io_backing_manager ;
109static char *tdb_file;
110static int actual_size;
111
112static unsigned char mtime_key[] = "filesystem MTIME";
113static unsigned char blksize_key[] = "filesystem BLKSIZE";
114static unsigned char uuid_key[] = "filesystem UUID";
115
116errcode_t set_undo_io_backing_manager(io_manager manager)
117{
118	/*
119	 * We may want to do some validation later
120	 */
121	undo_io_backing_manager = manager;
122	return 0;
123}
124
125errcode_t set_undo_io_backup_file(char *file_name)
126{
127	tdb_file = strdup(file_name);
128
129	if (tdb_file == NULL) {
130		return EXT2_ET_NO_MEMORY;
131	}
132
133	return 0;
134}
135
136static errcode_t write_file_system_identity(io_channel undo_channel,
137							TDB_CONTEXT *tdb)
138{
139	errcode_t retval;
140	struct ext2_super_block super;
141	TDB_DATA tdb_key, tdb_data;
142	struct undo_private_data *data;
143	io_channel channel;
144	int block_size ;
145
146	data = (struct undo_private_data *) undo_channel->private_data;
147	channel = data->real;
148	block_size = channel->block_size;
149
150	io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
151	retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
152	if (retval)
153		goto err_out;
154
155	/* Write to tdb file in the file system byte order */
156	tdb_key.dptr = mtime_key;
157	tdb_key.dsize = sizeof(mtime_key);
158	tdb_data.dptr = (unsigned char *) &(super.s_mtime);
159	tdb_data.dsize = sizeof(super.s_mtime);
160
161	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
162	if (retval == -1) {
163		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
164		goto err_out;
165	}
166
167	tdb_key.dptr = uuid_key;
168	tdb_key.dsize = sizeof(uuid_key);
169	tdb_data.dptr = (unsigned char *)&(super.s_uuid);
170	tdb_data.dsize = sizeof(super.s_uuid);
171
172	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
173	if (retval == -1) {
174		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
175	}
176
177err_out:
178	io_channel_set_blksize(channel, block_size);
179	return retval;
180}
181
182static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
183{
184	errcode_t retval;
185	TDB_DATA tdb_key, tdb_data;
186
187	tdb_key.dptr = blksize_key;
188	tdb_key.dsize = sizeof(blksize_key);
189	tdb_data.dptr = (unsigned char *)&(block_size);
190	tdb_data.dsize = sizeof(block_size);
191
192	retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
193	if (retval == -1) {
194		retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
195	}
196
197	return retval;
198}
199
200static errcode_t undo_write_tdb(io_channel channel,
201				unsigned long long block, int count)
202
203{
204	int size, sz;
205	unsigned long long block_num, backing_blk_num;
206	errcode_t retval = 0;
207	ext2_loff_t offset;
208	struct undo_private_data *data;
209	TDB_DATA tdb_key, tdb_data;
210	unsigned char *read_ptr;
211	unsigned long long end_block;
212
213	data = (struct undo_private_data *) channel->private_data;
214
215	if (data->tdb == NULL) {
216		/*
217		 * Transaction database not initialized
218		 */
219		return 0;
220	}
221
222	if (count == 1)
223		size = channel->block_size;
224	else {
225		if (count < 0)
226			size = -count;
227		else
228			size = count * channel->block_size;
229	}
230	/*
231	 * Data is stored in tdb database as blocks of tdb_data_size size
232	 * This helps in efficient lookup further.
233	 *
234	 * We divide the disk to blocks of tdb_data_size.
235	 */
236	offset = (block * channel->block_size) + data->offset ;
237	block_num = offset / data->tdb_data_size;
238	end_block = (offset + size) / data->tdb_data_size;
239
240	tdb_transaction_start(data->tdb);
241	while (block_num <= end_block ) {
242
243		tdb_key.dptr = (unsigned char *)&block_num;
244		tdb_key.dsize = sizeof(block_num);
245		/*
246		 * Check if we have the record already
247		 */
248		if (tdb_exists(data->tdb, tdb_key)) {
249			/* Try the next block */
250			block_num++;
251			continue;
252		}
253		/*
254		 * Read one block using the backing I/O manager
255		 * The backing I/O manager block size may be
256		 * different from the tdb_data_size.
257		 * Also we need to recalcuate the block number with respect
258		 * to the backing I/O manager.
259		 */
260		offset = block_num * data->tdb_data_size;
261		backing_blk_num = (offset - data->offset) / channel->block_size;
262
263		count = data->tdb_data_size +
264				((offset - data->offset) % channel->block_size);
265		retval = ext2fs_get_mem(count, &read_ptr);
266		if (retval) {
267			tdb_transaction_cancel(data->tdb);
268			return retval;
269		}
270
271		memset(read_ptr, 0, count);
272		actual_size = 0;
273		if ((count % channel->block_size) == 0)
274			sz = count / channel->block_size;
275		else
276			sz = -count;
277		retval = io_channel_read_blk64(data->real, backing_blk_num,
278					     sz, read_ptr);
279		if (retval) {
280			if (retval != EXT2_ET_SHORT_READ) {
281				free(read_ptr);
282				tdb_transaction_cancel(data->tdb);
283				return retval;
284			}
285			/*
286			 * short read so update the record size
287			 * accordingly
288			 */
289			tdb_data.dsize = actual_size;
290		} else {
291			tdb_data.dsize = data->tdb_data_size;
292		}
293		tdb_data.dptr = read_ptr +
294				((offset - data->offset) % channel->block_size);
295#ifdef DEBUG
296		printf("Printing with key %lld data %x and size %d\n",
297		       block_num,
298		       tdb_data.dptr,
299		       tdb_data.dsize);
300#endif
301		if (!data->tdb_written) {
302			data->tdb_written = 1;
303			/* Write the blocksize to tdb file */
304			retval = write_block_size(data->tdb,
305						  data->tdb_data_size);
306			if (retval) {
307				tdb_transaction_cancel(data->tdb);
308				retval = EXT2_ET_TDB_ERR_IO;
309				free(read_ptr);
310				return retval;
311			}
312		}
313		retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
314		if (retval == -1) {
315			/*
316			 * TDB_ERR_EXISTS cannot happen because we
317			 * have already verified it doesn't exist
318			 */
319			tdb_transaction_cancel(data->tdb);
320			retval = EXT2_ET_TDB_ERR_IO;
321			free(read_ptr);
322			return retval;
323		}
324		free(read_ptr);
325		/* Next block */
326		block_num++;
327	}
328	tdb_transaction_commit(data->tdb);
329
330	return retval;
331}
332
333static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
334				    unsigned long block ATTR((unused)),
335				    int count ATTR((unused)),
336				    void *data ATTR((unused)),
337				    size_t size ATTR((unused)),
338				    int actual,
339				    errcode_t error ATTR((unused)))
340{
341	actual_size = actual;
342	return error;
343}
344
345static void undo_err_handler_init(io_channel channel)
346{
347	channel->read_error = undo_io_read_error;
348}
349
350static errcode_t undo_open(const char *name, int flags, io_channel *channel)
351{
352	io_channel	io = NULL;
353	struct undo_private_data *data = NULL;
354	errcode_t	retval;
355
356	if (name == 0)
357		return EXT2_ET_BAD_DEVICE_NAME;
358	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
359	if (retval)
360		goto cleanup;
361	memset(io, 0, sizeof(struct struct_io_channel));
362	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
363	retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
364	if (retval)
365		goto cleanup;
366
367	io->manager = undo_io_manager;
368	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
369	if (retval)
370		goto cleanup;
371
372	strcpy(io->name, name);
373	io->private_data = data;
374	io->block_size = 1024;
375	io->read_error = 0;
376	io->write_error = 0;
377	io->refcount = 1;
378
379	memset(data, 0, sizeof(struct undo_private_data));
380	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
381
382	if (undo_io_backing_manager) {
383		retval = undo_io_backing_manager->open(name, flags,
384						       &data->real);
385		if (retval)
386			goto cleanup;
387	} else {
388		data->real = 0;
389	}
390
391	/* setup the tdb file */
392	data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
393			     O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
394	if (!data->tdb) {
395		retval = errno;
396		goto cleanup;
397	}
398
399	/*
400	 * setup err handler for read so that we know
401	 * when the backing manager fails do short read
402	 */
403	if (data->real)
404		undo_err_handler_init(data->real);
405
406	*channel = io;
407	return 0;
408
409cleanup:
410	if (data && data->real)
411		io_channel_close(data->real);
412	if (data)
413		ext2fs_free_mem(&data);
414	if (io)
415		ext2fs_free_mem(&io);
416	return retval;
417}
418
419static errcode_t undo_close(io_channel channel)
420{
421	struct undo_private_data *data;
422	errcode_t	retval = 0;
423
424	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
425	data = (struct undo_private_data *) channel->private_data;
426	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
427
428	if (--channel->refcount > 0)
429		return 0;
430	/* Before closing write the file system identity */
431	retval = write_file_system_identity(channel, data->tdb);
432	if (retval)
433		return retval;
434	if (data->real)
435		retval = io_channel_close(data->real);
436	if (data->tdb)
437		tdb_close(data->tdb);
438	ext2fs_free_mem(&channel->private_data);
439	if (channel->name)
440		ext2fs_free_mem(&channel->name);
441	ext2fs_free_mem(&channel);
442
443	return retval;
444}
445
446static errcode_t undo_set_blksize(io_channel channel, int blksize)
447{
448	struct undo_private_data *data;
449	errcode_t		retval = 0;
450
451	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
452	data = (struct undo_private_data *) channel->private_data;
453	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
454
455	if (data->real)
456		retval = io_channel_set_blksize(data->real, blksize);
457	/*
458	 * Set the block size used for tdb
459	 */
460	if (!data->tdb_data_size) {
461		data->tdb_data_size = blksize;
462	}
463	channel->block_size = blksize;
464	return retval;
465}
466
467static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
468			       int count, void *buf)
469{
470	errcode_t	retval = 0;
471	struct undo_private_data *data;
472
473	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
474	data = (struct undo_private_data *) channel->private_data;
475	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
476
477	if (data->real)
478		retval = io_channel_read_blk64(data->real, block, count, buf);
479
480	return retval;
481}
482
483static errcode_t undo_read_blk(io_channel channel, unsigned long block,
484			       int count, void *buf)
485{
486	return undo_read_blk64(channel, block, count, buf);
487}
488
489static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
490				int count, const void *buf)
491{
492	struct undo_private_data *data;
493	errcode_t	retval = 0;
494
495	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
496	data = (struct undo_private_data *) channel->private_data;
497	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
498	/*
499	 * First write the existing content into database
500	 */
501	retval = undo_write_tdb(channel, block, count);
502	if (retval)
503		 return retval;
504	if (data->real)
505		retval = io_channel_write_blk64(data->real, block, count, buf);
506
507	return retval;
508}
509
510static errcode_t undo_write_blk(io_channel channel, unsigned long block,
511				int count, const void *buf)
512{
513	return undo_write_blk64(channel, block, count, buf);
514}
515
516static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
517				 int size, const void *buf)
518{
519	struct undo_private_data *data;
520	errcode_t	retval = 0;
521	ext2_loff_t	location;
522	unsigned long blk_num, count;;
523
524	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
525	data = (struct undo_private_data *) channel->private_data;
526	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
527
528	location = offset + data->offset;
529	blk_num = location/channel->block_size;
530	/*
531	 * the size specified may spread across multiple blocks
532	 * also make sure we account for the fact that block start
533	 * offset for tdb is different from the backing I/O manager
534	 * due to possible different block size
535	 */
536	count = (size + (location % channel->block_size) +
537			channel->block_size  -1)/channel->block_size;
538	retval = undo_write_tdb(channel, blk_num, count);
539	if (retval)
540		return retval;
541	if (data->real && data->real->manager->write_byte)
542		retval = io_channel_write_byte(data->real, offset, size, buf);
543
544	return retval;
545}
546
547/*
548 * Flush data buffers to disk.
549 */
550static errcode_t undo_flush(io_channel channel)
551{
552	errcode_t	retval = 0;
553	struct undo_private_data *data;
554
555	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
556	data = (struct undo_private_data *) channel->private_data;
557	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
558
559	if (data->real)
560		retval = io_channel_flush(data->real);
561
562	return retval;
563}
564
565static errcode_t undo_set_option(io_channel channel, const char *option,
566				 const char *arg)
567{
568	errcode_t	retval = 0;
569	struct undo_private_data *data;
570	unsigned long tmp;
571	char *end;
572
573	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
574	data = (struct undo_private_data *) channel->private_data;
575	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
576
577	if (!strcmp(option, "tdb_data_size")) {
578		if (!arg)
579			return EXT2_ET_INVALID_ARGUMENT;
580
581		tmp = strtoul(arg, &end, 0);
582		if (*end)
583			return EXT2_ET_INVALID_ARGUMENT;
584		if (!data->tdb_data_size || !data->tdb_written) {
585			data->tdb_data_size = tmp;
586		}
587		return 0;
588	}
589	/*
590	 * Need to support offset option to work with
591	 * Unix I/O manager
592	 */
593	if (data->real && data->real->manager->set_option) {
594		retval = data->real->manager->set_option(data->real,
595							option, arg);
596	}
597	if (!retval && !strcmp(option, "offset")) {
598		if (!arg)
599			return EXT2_ET_INVALID_ARGUMENT;
600
601		tmp = strtoul(arg, &end, 0);
602		if (*end)
603			return EXT2_ET_INVALID_ARGUMENT;
604		data->offset = tmp;
605	}
606	return retval;
607}
608
609static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
610{
611	errcode_t	retval = 0;
612	struct undo_private_data *data;
613
614	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
615	data = (struct undo_private_data *) channel->private_data;
616	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
617
618	if (data->real)
619		retval = (data->real->manager->get_stats)(data->real, stats);
620
621	return retval;
622}
623