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
29 #include <sys/utsname.h>
35 #include <sys/types.h>
37 #if HAVE_SYS_RESOURCE_H
38 #include <sys/resource.h>
47 #define ATTR(x) __attribute__(x)
53 * For checking structure magic numbers...
56 #define EXT2_CHECK_MAGIC(struct, code) \
57 if ((struct)->magic != (code)) return (code)
59 struct undo_private_data {
64 /* The backing io channel */
70 /* to support offset in unix I/O manager */
74 static errcode_t undo_open(const char *name, int flags, io_channel *channel);
75 static errcode_t undo_close(io_channel channel);
76 static errcode_t undo_set_blksize(io_channel channel, int blksize);
77 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
78 int count, void *data);
79 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
80 int count, const void *data);
81 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
82 int count, void *data);
83 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
84 int count, const void *data);
85 static errcode_t undo_flush(io_channel channel);
86 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
87 int size, const void *data);
88 static errcode_t undo_set_option(io_channel channel, const char *option,
90 static errcode_t undo_get_stats(io_channel channel, io_stats *stats);
92 static struct struct_io_manager struct_undo_manager = {
93 EXT2_ET_MAGIC_IO_MANAGER,
108 io_manager undo_io_manager = &struct_undo_manager;
109 static io_manager undo_io_backing_manager ;
110 static char *tdb_file;
111 static int actual_size;
113 static unsigned char mtime_key[] = "filesystem MTIME";
114 static unsigned char blksize_key[] = "filesystem BLKSIZE";
115 static unsigned char uuid_key[] = "filesystem UUID";
117 errcode_t set_undo_io_backing_manager(io_manager manager)
120 * We may want to do some validation later
122 undo_io_backing_manager = manager;
126 errcode_t set_undo_io_backup_file(char *file_name)
128 tdb_file = strdup(file_name);
130 if (tdb_file == NULL) {
131 return EXT2_ET_NO_MEMORY;
137 static errcode_t write_file_system_identity(io_channel undo_channel,
141 struct ext2_super_block super;
142 TDB_DATA tdb_key, tdb_data;
143 struct undo_private_data *data;
147 data = (struct undo_private_data *) undo_channel->private_data;
148 channel = data->real;
149 block_size = channel->block_size;
151 io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
152 retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
156 /* Write to tdb file in the file system byte order */
157 tdb_key.dptr = mtime_key;
158 tdb_key.dsize = sizeof(mtime_key);
159 tdb_data.dptr = (unsigned char *) &(super.s_mtime);
160 tdb_data.dsize = sizeof(super.s_mtime);
162 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
164 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
168 tdb_key.dptr = uuid_key;
169 tdb_key.dsize = sizeof(uuid_key);
170 tdb_data.dptr = (unsigned char *)&(super.s_uuid);
171 tdb_data.dsize = sizeof(super.s_uuid);
173 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
175 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
179 io_channel_set_blksize(channel, block_size);
183 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
186 TDB_DATA tdb_key, tdb_data;
188 tdb_key.dptr = blksize_key;
189 tdb_key.dsize = sizeof(blksize_key);
190 tdb_data.dptr = (unsigned char *)&(block_size);
191 tdb_data.dsize = sizeof(block_size);
193 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
195 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
201 static errcode_t undo_write_tdb(io_channel channel,
202 unsigned long long block, int count)
206 unsigned long long block_num, backing_blk_num;
207 errcode_t retval = 0;
209 struct undo_private_data *data;
210 TDB_DATA tdb_key, tdb_data;
211 unsigned char *read_ptr;
212 unsigned long long end_block;
214 data = (struct undo_private_data *) channel->private_data;
216 if (data->tdb == NULL) {
218 * Transaction database not initialized
224 size = channel->block_size;
229 size = count * channel->block_size;
232 * Data is stored in tdb database as blocks of tdb_data_size size
233 * This helps in efficient lookup further.
235 * We divide the disk to blocks of tdb_data_size.
237 offset = (block * channel->block_size) + data->offset ;
238 block_num = offset / data->tdb_data_size;
239 end_block = (offset + size) / data->tdb_data_size;
241 tdb_transaction_start(data->tdb);
242 while (block_num <= end_block ) {
244 tdb_key.dptr = (unsigned char *)&block_num;
245 tdb_key.dsize = sizeof(block_num);
247 * Check if we have the record already
249 if (tdb_exists(data->tdb, tdb_key)) {
250 /* Try the next block */
255 * Read one block using the backing I/O manager
256 * The backing I/O manager block size may be
257 * different from the tdb_data_size.
258 * Also we need to recalcuate the block number with respect
259 * to the backing I/O manager.
261 offset = block_num * data->tdb_data_size;
262 backing_blk_num = (offset - data->offset) / channel->block_size;
264 count = data->tdb_data_size +
265 ((offset - data->offset) % channel->block_size);
266 retval = ext2fs_get_mem(count, &read_ptr);
268 tdb_transaction_cancel(data->tdb);
272 memset(read_ptr, 0, count);
274 if ((count % channel->block_size) == 0)
275 sz = count / channel->block_size;
278 retval = io_channel_read_blk64(data->real, backing_blk_num,
281 if (retval != EXT2_ET_SHORT_READ) {
283 tdb_transaction_cancel(data->tdb);
287 * short read so update the record size
290 tdb_data.dsize = actual_size;
292 tdb_data.dsize = data->tdb_data_size;
294 tdb_data.dptr = read_ptr +
295 ((offset - data->offset) % channel->block_size);
297 printf("Printing with key %lld data %x and size %d\n",
302 if (!data->tdb_written) {
303 data->tdb_written = 1;
304 /* Write the blocksize to tdb file */
305 retval = write_block_size(data->tdb,
306 data->tdb_data_size);
308 tdb_transaction_cancel(data->tdb);
309 retval = EXT2_ET_TDB_ERR_IO;
314 retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
317 * TDB_ERR_EXISTS cannot happen because we
318 * have already verified it doesn't exist
320 tdb_transaction_cancel(data->tdb);
321 retval = EXT2_ET_TDB_ERR_IO;
329 tdb_transaction_commit(data->tdb);
334 static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
335 unsigned long block ATTR((unused)),
336 int count ATTR((unused)),
337 void *data ATTR((unused)),
338 size_t size ATTR((unused)),
340 errcode_t error ATTR((unused)))
342 actual_size = actual;
346 static void undo_err_handler_init(io_channel channel)
348 channel->read_error = undo_io_read_error;
351 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
353 io_channel io = NULL;
354 struct undo_private_data *data = NULL;
358 return EXT2_ET_BAD_DEVICE_NAME;
359 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
362 memset(io, 0, sizeof(struct struct_io_channel));
363 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
364 retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
368 io->manager = undo_io_manager;
369 retval = ext2fs_get_mem(strlen(name)+1, &io->name);
373 strcpy(io->name, name);
374 io->private_data = data;
375 io->block_size = 1024;
380 memset(data, 0, sizeof(struct undo_private_data));
381 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
383 if (undo_io_backing_manager) {
384 retval = undo_io_backing_manager->open(name, flags,
392 /* setup the tdb file */
393 data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
394 O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
401 * setup err handler for read so that we know
402 * when the backing manager fails do short read
405 undo_err_handler_init(data->real);
411 if (data && data->real)
412 io_channel_close(data->real);
414 ext2fs_free_mem(&data);
416 ext2fs_free_mem(&io);
420 static errcode_t undo_close(io_channel channel)
422 struct undo_private_data *data;
423 errcode_t retval = 0;
425 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
426 data = (struct undo_private_data *) channel->private_data;
427 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
429 if (--channel->refcount > 0)
431 /* Before closing write the file system identity */
432 retval = write_file_system_identity(channel, data->tdb);
436 retval = io_channel_close(data->real);
438 tdb_close(data->tdb);
439 ext2fs_free_mem(&channel->private_data);
441 ext2fs_free_mem(&channel->name);
442 ext2fs_free_mem(&channel);
447 static errcode_t undo_set_blksize(io_channel channel, int blksize)
449 struct undo_private_data *data;
450 errcode_t retval = 0;
452 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
453 data = (struct undo_private_data *) channel->private_data;
454 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
457 retval = io_channel_set_blksize(data->real, blksize);
459 * Set the block size used for tdb
461 if (!data->tdb_data_size) {
462 data->tdb_data_size = blksize;
464 channel->block_size = blksize;
468 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
469 int count, void *buf)
471 errcode_t retval = 0;
472 struct undo_private_data *data;
474 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
475 data = (struct undo_private_data *) channel->private_data;
476 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
479 retval = io_channel_read_blk64(data->real, block, count, buf);
484 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
485 int count, void *buf)
487 return undo_read_blk64(channel, block, count, buf);
490 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
491 int count, const void *buf)
493 struct undo_private_data *data;
494 errcode_t retval = 0;
496 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
497 data = (struct undo_private_data *) channel->private_data;
498 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
500 * First write the existing content into database
502 retval = undo_write_tdb(channel, block, count);
506 retval = io_channel_write_blk64(data->real, block, count, buf);
511 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
512 int count, const void *buf)
514 return undo_write_blk64(channel, block, count, buf);
517 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
518 int size, const void *buf)
520 struct undo_private_data *data;
521 errcode_t retval = 0;
522 ext2_loff_t location;
523 unsigned long blk_num, count;;
525 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
526 data = (struct undo_private_data *) channel->private_data;
527 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
529 location = offset + data->offset;
530 blk_num = location/channel->block_size;
532 * the size specified may spread across multiple blocks
533 * also make sure we account for the fact that block start
534 * offset for tdb is different from the backing I/O manager
535 * due to possible different block size
537 count = (size + (location % channel->block_size) +
538 channel->block_size -1)/channel->block_size;
539 retval = undo_write_tdb(channel, blk_num, count);
542 if (data->real && data->real->manager->write_byte)
543 retval = io_channel_write_byte(data->real, offset, size, buf);
549 * Flush data buffers to disk.
551 static errcode_t undo_flush(io_channel channel)
553 errcode_t retval = 0;
554 struct undo_private_data *data;
556 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
557 data = (struct undo_private_data *) channel->private_data;
558 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
561 retval = io_channel_flush(data->real);
566 static errcode_t undo_set_option(io_channel channel, const char *option,
569 errcode_t retval = 0;
570 struct undo_private_data *data;
574 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
575 data = (struct undo_private_data *) channel->private_data;
576 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
578 if (!strcmp(option, "tdb_data_size")) {
580 return EXT2_ET_INVALID_ARGUMENT;
582 tmp = strtoul(arg, &end, 0);
584 return EXT2_ET_INVALID_ARGUMENT;
585 if (!data->tdb_data_size || !data->tdb_written) {
586 data->tdb_data_size = tmp;
591 * Need to support offset option to work with
594 if (data->real && data->real->manager->set_option) {
595 retval = data->real->manager->set_option(data->real,
598 if (!retval && !strcmp(option, "offset")) {
600 return EXT2_ET_INVALID_ARGUMENT;
602 tmp = strtoul(arg, &end, 0);
604 return EXT2_ET_INVALID_ARGUMENT;
610 static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
612 errcode_t retval = 0;
613 struct undo_private_data *data;
615 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
616 data = (struct undo_private_data *) channel->private_data;
617 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
620 retval = (data->real->manager->get_stats)(data->real, stats);