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
5 * Copyright IBM Corporation, 2007
6 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
9 * This file may be redistributed under the terms of the GNU Library
10 * General Public License, version 2.
14 #define _LARGEFILE_SOURCE
15 #define _LARGEFILE64_SOURCE
28 #include <sys/utsname.h>
34 #include <sys/types.h>
36 #if HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
46 #define ATTR(x) __attribute__(x)
52 * For checking structure magic numbers...
55 #define EXT2_CHECK_MAGIC(struct, code) \
56 if ((struct)->magic != (code)) return (code)
58 struct undo_private_data {
63 /* The backing io channel */
69 /* to support offset in unix I/O manager */
73 static errcode_t undo_open(const char *name, int flags, io_channel *channel);
74 static errcode_t undo_close(io_channel channel);
75 static errcode_t undo_set_blksize(io_channel channel, int blksize);
76 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
77 int count, void *data);
78 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
79 int count, const void *data);
80 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
81 int count, void *data);
82 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
83 int count, const void *data);
84 static errcode_t undo_flush(io_channel channel);
85 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
86 int size, const void *data);
87 static errcode_t undo_set_option(io_channel channel, const char *option,
89 static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
91 static struct struct_io_manager struct_undo_manager = {
92 EXT2_ET_MAGIC_IO_MANAGER,
107 io_manager undo_io_manager = &struct_undo_manager;
108 static io_manager undo_io_backing_manager ;
109 static char *tdb_file;
110 static int actual_size;
112 static unsigned char mtime_key[] = "filesystem MTIME";
113 static unsigned char blksize_key[] = "filesystem BLKSIZE";
114 static unsigned char uuid_key[] = "filesystem UUID";
116 errcode_t set_undo_io_backing_manager(io_manager manager)
119 * We may want to do some validation later
121 undo_io_backing_manager = manager;
125 errcode_t set_undo_io_backup_file(char *file_name)
127 tdb_file = strdup(file_name);
129 if (tdb_file == NULL) {
130 return EXT2_ET_NO_MEMORY;
136 static errcode_t write_file_system_identity(io_channel undo_channel,
140 struct ext2_super_block super;
141 TDB_DATA tdb_key, tdb_data;
142 struct undo_private_data *data;
146 data = (struct undo_private_data *) undo_channel->private_data;
147 channel = data->real;
148 block_size = channel->block_size;
150 io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
151 retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
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);
161 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
163 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
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);
172 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
174 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
178 io_channel_set_blksize(channel, block_size);
182 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
185 TDB_DATA tdb_key, tdb_data;
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);
192 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
194 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
200 static errcode_t undo_write_tdb(io_channel channel,
201 unsigned long long block, int count)
205 unsigned long long block_num, backing_blk_num;
206 errcode_t retval = 0;
208 struct undo_private_data *data;
209 TDB_DATA tdb_key, tdb_data;
210 unsigned char *read_ptr;
211 unsigned long long end_block;
213 data = (struct undo_private_data *) channel->private_data;
215 if (data->tdb == NULL) {
217 * Transaction database not initialized
223 size = channel->block_size;
228 size = count * channel->block_size;
231 * Data is stored in tdb database as blocks of tdb_data_size size
232 * This helps in efficient lookup further.
234 * We divide the disk to blocks of tdb_data_size.
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;
240 tdb_transaction_start(data->tdb);
241 while (block_num <= end_block ) {
243 tdb_key.dptr = (unsigned char *)&block_num;
244 tdb_key.dsize = sizeof(block_num);
246 * Check if we have the record already
248 if (tdb_exists(data->tdb, tdb_key)) {
249 /* Try the next block */
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.
260 offset = block_num * data->tdb_data_size;
261 backing_blk_num = (offset - data->offset) / channel->block_size;
263 count = data->tdb_data_size +
264 ((offset - data->offset) % channel->block_size);
265 retval = ext2fs_get_mem(count, &read_ptr);
267 tdb_transaction_cancel(data->tdb);
271 memset(read_ptr, 0, count);
273 if ((count % channel->block_size) == 0)
274 sz = count / channel->block_size;
277 retval = io_channel_read_blk64(data->real, backing_blk_num,
280 if (retval != EXT2_ET_SHORT_READ) {
282 tdb_transaction_cancel(data->tdb);
286 * short read so update the record size
289 tdb_data.dsize = actual_size;
291 tdb_data.dsize = data->tdb_data_size;
293 tdb_data.dptr = read_ptr +
294 ((offset - data->offset) % channel->block_size);
296 printf("Printing with key %lld data %x and size %d\n",
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);
307 tdb_transaction_cancel(data->tdb);
308 retval = EXT2_ET_TDB_ERR_IO;
313 retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
316 * TDB_ERR_EXISTS cannot happen because we
317 * have already verified it doesn't exist
319 tdb_transaction_cancel(data->tdb);
320 retval = EXT2_ET_TDB_ERR_IO;
328 tdb_transaction_commit(data->tdb);
333 static 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)),
339 errcode_t error ATTR((unused)))
341 actual_size = actual;
345 static void undo_err_handler_init(io_channel channel)
347 channel->read_error = undo_io_read_error;
350 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
352 io_channel io = NULL;
353 struct undo_private_data *data = NULL;
357 return EXT2_ET_BAD_DEVICE_NAME;
358 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
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);
367 io->manager = undo_io_manager;
368 retval = ext2fs_get_mem(strlen(name)+1, &io->name);
372 strcpy(io->name, name);
373 io->private_data = data;
374 io->block_size = 1024;
379 memset(data, 0, sizeof(struct undo_private_data));
380 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
382 if (undo_io_backing_manager) {
383 retval = undo_io_backing_manager->open(name, flags,
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);
400 * setup err handler for read so that we know
401 * when the backing manager fails do short read
404 undo_err_handler_init(data->real);
411 io_channel_close(data->real);
413 ext2fs_free_mem(&data);
415 ext2fs_free_mem(&io);
419 static errcode_t undo_close(io_channel channel)
421 struct undo_private_data *data;
422 errcode_t retval = 0;
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);
428 if (--channel->refcount > 0)
430 /* Before closing write the file system identity */
431 retval = write_file_system_identity(channel, data->tdb);
435 retval = io_channel_close(data->real);
437 tdb_close(data->tdb);
438 ext2fs_free_mem(&channel->private_data);
440 ext2fs_free_mem(&channel->name);
441 ext2fs_free_mem(&channel);
446 static errcode_t undo_set_blksize(io_channel channel, int blksize)
448 struct undo_private_data *data;
449 errcode_t retval = 0;
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);
456 retval = io_channel_set_blksize(data->real, blksize);
458 * Set the block size used for tdb
460 if (!data->tdb_data_size) {
461 data->tdb_data_size = blksize;
463 channel->block_size = blksize;
467 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
468 int count, void *buf)
470 errcode_t retval = 0;
471 struct undo_private_data *data;
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);
478 retval = io_channel_read_blk64(data->real, block, count, buf);
483 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
484 int count, void *buf)
486 return undo_read_blk64(channel, block, count, buf);
489 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
490 int count, const void *buf)
492 struct undo_private_data *data;
493 errcode_t retval = 0;
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);
499 * First write the existing content into database
501 retval = undo_write_tdb(channel, block, count);
505 retval = io_channel_write_blk64(data->real, block, count, buf);
510 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
511 int count, const void *buf)
513 return undo_write_blk64(channel, block, count, buf);
516 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
517 int size, const void *buf)
519 struct undo_private_data *data;
520 errcode_t retval = 0;
521 ext2_loff_t location;
522 unsigned long blk_num, count;;
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);
528 location = offset + data->offset;
529 blk_num = location/channel->block_size;
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
536 count = (size + (location % channel->block_size) +
537 channel->block_size -1)/channel->block_size;
538 retval = undo_write_tdb(channel, blk_num, count);
541 if (data->real && data->real->manager->write_byte)
542 retval = io_channel_write_byte(data->real, offset, size, buf);
548 * Flush data buffers to disk.
550 static errcode_t undo_flush(io_channel channel)
552 errcode_t retval = 0;
553 struct undo_private_data *data;
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);
560 retval = io_channel_flush(data->real);
565 static errcode_t undo_set_option(io_channel channel, const char *option,
568 errcode_t retval = 0;
569 struct undo_private_data *data;
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);
577 if (!strcmp(option, "tdb_data_size")) {
579 return EXT2_ET_INVALID_ARGUMENT;
581 tmp = strtoul(arg, &end, 0);
583 return EXT2_ET_INVALID_ARGUMENT;
584 if (!data->tdb_data_size || !data->tdb_written) {
585 data->tdb_data_size = tmp;
590 * Need to support offset option to work with
593 if (data->real && data->real->manager->set_option) {
594 retval = data->real->manager->set_option(data->real,
597 if (!retval && !strcmp(option, "offset")) {
599 return EXT2_ET_INVALID_ARGUMENT;
601 tmp = strtoul(arg, &end, 0);
603 return EXT2_ET_INVALID_ARGUMENT;
609 static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
611 errcode_t retval = 0;
612 struct undo_private_data *data;
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);
619 retval = (data->real->manager->get_stats)(data->real, stats);