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 Public
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
403 undo_err_handler_init(data->real);
410 io_channel_close(data->real);
412 ext2fs_free_mem(&data);
414 ext2fs_free_mem(&io);
418 static errcode_t undo_close(io_channel channel)
420 struct undo_private_data *data;
421 errcode_t retval = 0;
423 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
424 data = (struct undo_private_data *) channel->private_data;
425 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
427 if (--channel->refcount > 0)
429 /* Before closing write the file system identity */
430 retval = write_file_system_identity(channel, data->tdb);
434 retval = io_channel_close(data->real);
436 tdb_close(data->tdb);
437 ext2fs_free_mem(&channel->private_data);
439 ext2fs_free_mem(&channel->name);
440 ext2fs_free_mem(&channel);
445 static errcode_t undo_set_blksize(io_channel channel, int blksize)
447 struct undo_private_data *data;
448 errcode_t retval = 0;
450 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
451 data = (struct undo_private_data *) channel->private_data;
452 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
455 retval = io_channel_set_blksize(data->real, blksize);
457 * Set the block size used for tdb
459 if (!data->tdb_data_size) {
460 data->tdb_data_size = blksize;
462 channel->block_size = blksize;
466 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
467 int count, void *buf)
469 errcode_t retval = 0;
470 struct undo_private_data *data;
472 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
473 data = (struct undo_private_data *) channel->private_data;
474 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
477 retval = io_channel_read_blk64(data->real, block, count, buf);
482 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
483 int count, void *buf)
485 return undo_read_blk64(channel, block, count, buf);
488 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
489 int count, const void *buf)
491 struct undo_private_data *data;
492 errcode_t retval = 0;
494 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
495 data = (struct undo_private_data *) channel->private_data;
496 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
498 * First write the existing content into database
500 retval = undo_write_tdb(channel, block, count);
504 retval = io_channel_write_blk64(data->real, block, count, buf);
509 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
510 int count, const void *buf)
512 return undo_write_blk64(channel, block, count, buf);
515 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
516 int size, const void *buf)
518 struct undo_private_data *data;
519 errcode_t retval = 0;
520 ext2_loff_t location;
521 unsigned long blk_num, count;;
523 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
524 data = (struct undo_private_data *) channel->private_data;
525 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
527 location = offset + data->offset;
528 blk_num = location/channel->block_size;
530 * the size specified may spread across multiple blocks
531 * also make sure we account for the fact that block start
532 * offset for tdb is different from the backing I/O manager
533 * due to possible different block size
535 count = (size + (location % channel->block_size) +
536 channel->block_size -1)/channel->block_size;
537 retval = undo_write_tdb(channel, blk_num, count);
540 if (data->real && data->real->manager->write_byte)
541 retval = io_channel_write_byte(data->real, offset, size, buf);
547 * Flush data buffers to disk.
549 static errcode_t undo_flush(io_channel channel)
551 errcode_t retval = 0;
552 struct undo_private_data *data;
554 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
555 data = (struct undo_private_data *) channel->private_data;
556 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
559 retval = io_channel_flush(data->real);
564 static errcode_t undo_set_option(io_channel channel, const char *option,
567 errcode_t retval = 0;
568 struct undo_private_data *data;
572 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
573 data = (struct undo_private_data *) channel->private_data;
574 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
576 if (!strcmp(option, "tdb_data_size")) {
578 return EXT2_ET_INVALID_ARGUMENT;
580 tmp = strtoul(arg, &end, 0);
582 return EXT2_ET_INVALID_ARGUMENT;
583 if (!data->tdb_data_size || !data->tdb_written) {
584 data->tdb_data_size = tmp;
589 * Need to support offset option to work with
592 if (data->real && data->real->manager->set_option) {
593 retval = data->real->manager->set_option(data->real,
596 if (!retval && !strcmp(option, "offset")) {
598 return EXT2_ET_INVALID_ARGUMENT;
600 tmp = strtoul(arg, &end, 0);
602 return EXT2_ET_INVALID_ARGUMENT;
608 static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
610 errcode_t retval = 0;
611 struct undo_private_data *data;
613 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
614 data = (struct undo_private_data *) channel->private_data;
615 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
618 retval = (data->real->manager->get_stats)(data->real, stats);