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 io_manager undo_io_backing_manager;
75 static char *tdb_file;
76 static int actual_size;
78 static unsigned char mtime_key[] = "filesystem MTIME";
79 static unsigned char blksize_key[] = "filesystem BLKSIZE";
80 static unsigned char uuid_key[] = "filesystem UUID";
82 errcode_t set_undo_io_backing_manager(io_manager manager)
85 * We may want to do some validation later
87 undo_io_backing_manager = manager;
91 errcode_t set_undo_io_backup_file(char *file_name)
93 tdb_file = strdup(file_name);
95 if (tdb_file == NULL) {
96 return EXT2_ET_NO_MEMORY;
102 static errcode_t write_file_system_identity(io_channel undo_channel,
106 struct ext2_super_block super;
107 TDB_DATA tdb_key, tdb_data;
108 struct undo_private_data *data;
112 data = (struct undo_private_data *) undo_channel->private_data;
113 channel = data->real;
114 block_size = channel->block_size;
116 io_channel_set_blksize(channel, SUPERBLOCK_OFFSET);
117 retval = io_channel_read_blk64(channel, 1, -SUPERBLOCK_SIZE, &super);
121 /* Write to tdb file in the file system byte order */
122 tdb_key.dptr = mtime_key;
123 tdb_key.dsize = sizeof(mtime_key);
124 tdb_data.dptr = (unsigned char *) &(super.s_mtime);
125 tdb_data.dsize = sizeof(super.s_mtime);
127 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
129 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
133 tdb_key.dptr = uuid_key;
134 tdb_key.dsize = sizeof(uuid_key);
135 tdb_data.dptr = (unsigned char *)&(super.s_uuid);
136 tdb_data.dsize = sizeof(super.s_uuid);
138 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
140 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
144 io_channel_set_blksize(channel, block_size);
148 static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size)
151 TDB_DATA tdb_key, tdb_data;
153 tdb_key.dptr = blksize_key;
154 tdb_key.dsize = sizeof(blksize_key);
155 tdb_data.dptr = (unsigned char *)&(block_size);
156 tdb_data.dsize = sizeof(block_size);
158 retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT);
160 retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb);
166 static errcode_t undo_write_tdb(io_channel channel,
167 unsigned long long block, int count)
171 unsigned long long block_num, backing_blk_num;
172 errcode_t retval = 0;
174 struct undo_private_data *data;
175 TDB_DATA tdb_key, tdb_data;
176 unsigned char *read_ptr;
177 unsigned long long end_block;
179 data = (struct undo_private_data *) channel->private_data;
181 if (data->tdb == NULL) {
183 * Transaction database not initialized
189 size = channel->block_size;
194 size = count * channel->block_size;
197 * Data is stored in tdb database as blocks of tdb_data_size size
198 * This helps in efficient lookup further.
200 * We divide the disk to blocks of tdb_data_size.
202 offset = (block * channel->block_size) + data->offset ;
203 block_num = offset / data->tdb_data_size;
204 end_block = (offset + size) / data->tdb_data_size;
206 tdb_transaction_start(data->tdb);
207 while (block_num <= end_block ) {
209 tdb_key.dptr = (unsigned char *)&block_num;
210 tdb_key.dsize = sizeof(block_num);
212 * Check if we have the record already
214 if (tdb_exists(data->tdb, tdb_key)) {
215 /* Try the next block */
220 * Read one block using the backing I/O manager
221 * The backing I/O manager block size may be
222 * different from the tdb_data_size.
223 * Also we need to recalcuate the block number with respect
224 * to the backing I/O manager.
226 offset = block_num * data->tdb_data_size;
227 backing_blk_num = (offset - data->offset) / channel->block_size;
229 count = data->tdb_data_size +
230 ((offset - data->offset) % channel->block_size);
231 retval = ext2fs_get_mem(count, &read_ptr);
233 tdb_transaction_cancel(data->tdb);
237 memset(read_ptr, 0, count);
239 if ((count % channel->block_size) == 0)
240 sz = count / channel->block_size;
243 retval = io_channel_read_blk64(data->real, backing_blk_num,
246 if (retval != EXT2_ET_SHORT_READ) {
248 tdb_transaction_cancel(data->tdb);
252 * short read so update the record size
255 tdb_data.dsize = actual_size;
257 tdb_data.dsize = data->tdb_data_size;
259 tdb_data.dptr = read_ptr +
260 ((offset - data->offset) % channel->block_size);
262 printf("Printing with key %lld data %x and size %d\n",
267 if (!data->tdb_written) {
268 data->tdb_written = 1;
269 /* Write the blocksize to tdb file */
270 retval = write_block_size(data->tdb,
271 data->tdb_data_size);
273 tdb_transaction_cancel(data->tdb);
274 retval = EXT2_ET_TDB_ERR_IO;
279 retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT);
282 * TDB_ERR_EXISTS cannot happen because we
283 * have already verified it doesn't exist
285 tdb_transaction_cancel(data->tdb);
286 retval = EXT2_ET_TDB_ERR_IO;
294 tdb_transaction_commit(data->tdb);
299 static errcode_t undo_io_read_error(io_channel channel ATTR((unused)),
300 unsigned long block ATTR((unused)),
301 int count ATTR((unused)),
302 void *data ATTR((unused)),
303 size_t size ATTR((unused)),
305 errcode_t error ATTR((unused)))
307 actual_size = actual;
311 static void undo_err_handler_init(io_channel channel)
313 channel->read_error = undo_io_read_error;
316 static errcode_t undo_open(const char *name, int flags, io_channel *channel)
318 io_channel io = NULL;
319 struct undo_private_data *data = NULL;
323 return EXT2_ET_BAD_DEVICE_NAME;
324 retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
327 memset(io, 0, sizeof(struct struct_io_channel));
328 io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
329 retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data);
333 io->manager = undo_io_manager;
334 retval = ext2fs_get_mem(strlen(name)+1, &io->name);
338 strcpy(io->name, name);
339 io->private_data = data;
340 io->block_size = 1024;
345 memset(data, 0, sizeof(struct undo_private_data));
346 data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
348 if (undo_io_backing_manager) {
349 retval = undo_io_backing_manager->open(name, flags,
357 /* setup the tdb file */
358 data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST,
359 O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
366 * setup err handler for read so that we know
367 * when the backing manager fails do short read
370 undo_err_handler_init(data->real);
376 if (data && data->real)
377 io_channel_close(data->real);
379 ext2fs_free_mem(&data);
381 ext2fs_free_mem(&io);
385 static errcode_t undo_close(io_channel channel)
387 struct undo_private_data *data;
388 errcode_t retval = 0;
390 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
391 data = (struct undo_private_data *) channel->private_data;
392 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
394 if (--channel->refcount > 0)
396 /* Before closing write the file system identity */
397 retval = write_file_system_identity(channel, data->tdb);
401 retval = io_channel_close(data->real);
403 tdb_close(data->tdb);
404 ext2fs_free_mem(&channel->private_data);
406 ext2fs_free_mem(&channel->name);
407 ext2fs_free_mem(&channel);
412 static errcode_t undo_set_blksize(io_channel channel, int blksize)
414 struct undo_private_data *data;
415 errcode_t retval = 0;
417 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
418 data = (struct undo_private_data *) channel->private_data;
419 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
422 retval = io_channel_set_blksize(data->real, blksize);
424 * Set the block size used for tdb
426 if (!data->tdb_data_size) {
427 data->tdb_data_size = blksize;
429 channel->block_size = blksize;
433 static errcode_t undo_read_blk64(io_channel channel, unsigned long long block,
434 int count, void *buf)
436 errcode_t retval = 0;
437 struct undo_private_data *data;
439 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
440 data = (struct undo_private_data *) channel->private_data;
441 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
444 retval = io_channel_read_blk64(data->real, block, count, buf);
449 static errcode_t undo_read_blk(io_channel channel, unsigned long block,
450 int count, void *buf)
452 return undo_read_blk64(channel, block, count, buf);
455 static errcode_t undo_write_blk64(io_channel channel, unsigned long long block,
456 int count, const void *buf)
458 struct undo_private_data *data;
459 errcode_t retval = 0;
461 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
462 data = (struct undo_private_data *) channel->private_data;
463 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
465 * First write the existing content into database
467 retval = undo_write_tdb(channel, block, count);
471 retval = io_channel_write_blk64(data->real, block, count, buf);
476 static errcode_t undo_write_blk(io_channel channel, unsigned long block,
477 int count, const void *buf)
479 return undo_write_blk64(channel, block, count, buf);
482 static errcode_t undo_write_byte(io_channel channel, unsigned long offset,
483 int size, const void *buf)
485 struct undo_private_data *data;
486 errcode_t retval = 0;
487 ext2_loff_t location;
488 unsigned long blk_num, count;;
490 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
491 data = (struct undo_private_data *) channel->private_data;
492 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
494 location = offset + data->offset;
495 blk_num = location/channel->block_size;
497 * the size specified may spread across multiple blocks
498 * also make sure we account for the fact that block start
499 * offset for tdb is different from the backing I/O manager
500 * due to possible different block size
502 count = (size + (location % channel->block_size) +
503 channel->block_size -1)/channel->block_size;
504 retval = undo_write_tdb(channel, blk_num, count);
507 if (data->real && data->real->manager->write_byte)
508 retval = io_channel_write_byte(data->real, offset, size, buf);
514 * Flush data buffers to disk.
516 static errcode_t undo_flush(io_channel channel)
518 errcode_t retval = 0;
519 struct undo_private_data *data;
521 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
522 data = (struct undo_private_data *) channel->private_data;
523 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
526 retval = io_channel_flush(data->real);
531 static errcode_t undo_set_option(io_channel channel, const char *option,
534 errcode_t retval = 0;
535 struct undo_private_data *data;
539 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
540 data = (struct undo_private_data *) channel->private_data;
541 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
543 if (!strcmp(option, "tdb_data_size")) {
545 return EXT2_ET_INVALID_ARGUMENT;
547 tmp = strtoul(arg, &end, 0);
549 return EXT2_ET_INVALID_ARGUMENT;
550 if (!data->tdb_data_size || !data->tdb_written) {
551 data->tdb_data_size = tmp;
556 * Need to support offset option to work with
559 if (data->real && data->real->manager->set_option) {
560 retval = data->real->manager->set_option(data->real,
563 if (!retval && !strcmp(option, "offset")) {
565 return EXT2_ET_INVALID_ARGUMENT;
567 tmp = strtoul(arg, &end, 0);
569 return EXT2_ET_INVALID_ARGUMENT;
575 static errcode_t undo_get_stats(io_channel channel, io_stats *stats)
577 errcode_t retval = 0;
578 struct undo_private_data *data;
580 EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
581 data = (struct undo_private_data *) channel->private_data;
582 EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
585 retval = (data->real->manager->get_stats)(data->real, stats);
590 static struct struct_io_manager struct_undo_manager = {
591 .magic = EXT2_ET_MAGIC_IO_MANAGER,
592 .name = "Undo I/O Manager",
595 .set_blksize = undo_set_blksize,
596 .read_blk = undo_read_blk,
597 .write_blk = undo_write_blk,
599 .write_byte = undo_write_byte,
600 .set_option = undo_set_option,
601 .get_stats = undo_get_stats,
602 .read_blk64 = undo_read_blk64,
603 .write_blk64 = undo_write_blk64,
606 io_manager undo_io_manager = &struct_undo_manager;